power-grid-model-ds 0.0.1a11709467271__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- power_grid_model_ds/__init__.py +9 -0
- power_grid_model_ds/_core/__init__.py +0 -0
- power_grid_model_ds/_core/data_source/__init__.py +0 -0
- power_grid_model_ds/_core/data_source/generator/__init__.py +0 -0
- power_grid_model_ds/_core/data_source/generator/arrays/__init__.py +0 -0
- power_grid_model_ds/_core/data_source/generator/arrays/base.py +25 -0
- power_grid_model_ds/_core/data_source/generator/arrays/line.py +133 -0
- power_grid_model_ds/_core/data_source/generator/arrays/node.py +37 -0
- power_grid_model_ds/_core/data_source/generator/arrays/source.py +30 -0
- power_grid_model_ds/_core/data_source/generator/arrays/transformer.py +37 -0
- power_grid_model_ds/_core/data_source/generator/grid_generators.py +78 -0
- power_grid_model_ds/_core/fancypy.py +66 -0
- power_grid_model_ds/_core/load_flow.py +140 -0
- power_grid_model_ds/_core/model/__init__.py +0 -0
- power_grid_model_ds/_core/model/arrays/__init__.py +43 -0
- power_grid_model_ds/_core/model/arrays/base/__init__.py +0 -0
- power_grid_model_ds/_core/model/arrays/base/_build.py +166 -0
- power_grid_model_ds/_core/model/arrays/base/_filters.py +115 -0
- power_grid_model_ds/_core/model/arrays/base/_modify.py +64 -0
- power_grid_model_ds/_core/model/arrays/base/_optional.py +11 -0
- power_grid_model_ds/_core/model/arrays/base/_string.py +94 -0
- power_grid_model_ds/_core/model/arrays/base/array.py +325 -0
- power_grid_model_ds/_core/model/arrays/base/errors.py +17 -0
- power_grid_model_ds/_core/model/arrays/pgm_arrays.py +122 -0
- power_grid_model_ds/_core/model/constants.py +27 -0
- power_grid_model_ds/_core/model/containers/__init__.py +0 -0
- power_grid_model_ds/_core/model/containers/base.py +244 -0
- power_grid_model_ds/_core/model/containers/grid_protocol.py +22 -0
- power_grid_model_ds/_core/model/dtypes/__init__.py +0 -0
- power_grid_model_ds/_core/model/dtypes/appliances.py +39 -0
- power_grid_model_ds/_core/model/dtypes/branches.py +117 -0
- power_grid_model_ds/_core/model/dtypes/id.py +19 -0
- power_grid_model_ds/_core/model/dtypes/nodes.py +27 -0
- power_grid_model_ds/_core/model/dtypes/regulators.py +30 -0
- power_grid_model_ds/_core/model/dtypes/sensors.py +63 -0
- power_grid_model_ds/_core/model/enums/__init__.py +0 -0
- power_grid_model_ds/_core/model/enums/nodes.py +16 -0
- power_grid_model_ds/_core/model/graphs/__init__.py +0 -0
- power_grid_model_ds/_core/model/graphs/container.py +158 -0
- power_grid_model_ds/_core/model/graphs/errors.py +19 -0
- power_grid_model_ds/_core/model/graphs/models/__init__.py +7 -0
- power_grid_model_ds/_core/model/graphs/models/_rustworkx_search.py +63 -0
- power_grid_model_ds/_core/model/graphs/models/base.py +326 -0
- power_grid_model_ds/_core/model/graphs/models/rustworkx.py +119 -0
- power_grid_model_ds/_core/model/grids/__init__.py +0 -0
- power_grid_model_ds/_core/model/grids/_text_sources.py +119 -0
- power_grid_model_ds/_core/model/grids/base.py +434 -0
- power_grid_model_ds/_core/model/grids/helpers.py +122 -0
- power_grid_model_ds/_core/utils/__init__.py +0 -0
- power_grid_model_ds/_core/utils/misc.py +41 -0
- power_grid_model_ds/_core/utils/pickle.py +47 -0
- power_grid_model_ds/_core/utils/zip.py +72 -0
- power_grid_model_ds/arrays.py +39 -0
- power_grid_model_ds/constants.py +7 -0
- power_grid_model_ds/enums.py +7 -0
- power_grid_model_ds/errors.py +27 -0
- power_grid_model_ds/fancypy.py +9 -0
- power_grid_model_ds/generators.py +11 -0
- power_grid_model_ds/graph_models.py +8 -0
- power_grid_model_ds-0.0.1a11709467271.dist-info/LICENSE +292 -0
- power_grid_model_ds-0.0.1a11709467271.dist-info/METADATA +80 -0
- power_grid_model_ds-0.0.1a11709467271.dist-info/RECORD +64 -0
- power_grid_model_ds-0.0.1a11709467271.dist-info/WHEEL +5 -0
- power_grid_model_ds-0.0.1a11709467271.dist-info/top_level.txt +1 -0
@@ -0,0 +1,244 @@
|
|
1
|
+
# SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
|
2
|
+
#
|
3
|
+
# SPDX-License-Identifier: MPL-2.0
|
4
|
+
|
5
|
+
"""Stores the FancyArrayContainer class"""
|
6
|
+
|
7
|
+
import dataclasses
|
8
|
+
import inspect
|
9
|
+
import logging
|
10
|
+
from dataclasses import dataclass
|
11
|
+
from typing import Type, TypeVar
|
12
|
+
|
13
|
+
import numpy as np
|
14
|
+
|
15
|
+
from power_grid_model_ds._core import fancypy as fp
|
16
|
+
from power_grid_model_ds._core.model.arrays.base.array import FancyArray
|
17
|
+
from power_grid_model_ds._core.model.arrays.base.errors import RecordDoesNotExist
|
18
|
+
from power_grid_model_ds._core.model.constants import EMPTY_ID
|
19
|
+
|
20
|
+
Self = TypeVar("Self", bound="FancyArrayContainer")
|
21
|
+
|
22
|
+
|
23
|
+
@dataclass
|
24
|
+
class FancyArrayContainer:
|
25
|
+
"""
|
26
|
+
Base class for ArrayContainers.
|
27
|
+
Contains general functionality that is nonspecific to the type of array being stored.
|
28
|
+
"""
|
29
|
+
|
30
|
+
_id_counter: int
|
31
|
+
|
32
|
+
@property
|
33
|
+
def id_counter(self):
|
34
|
+
"""Returns the private _id_counter field (as read-only)"""
|
35
|
+
return self._id_counter
|
36
|
+
|
37
|
+
@classmethod
|
38
|
+
def empty(cls: Type[Self]) -> Self:
|
39
|
+
"""Create an empty grid"""
|
40
|
+
empty_fields = cls._get_empty_fields()
|
41
|
+
return cls(**empty_fields)
|
42
|
+
|
43
|
+
def all_arrays(self):
|
44
|
+
"""Returns all arrays in the container."""
|
45
|
+
|
46
|
+
for field in dataclasses.fields(self):
|
47
|
+
attribute = getattr(self, field.name)
|
48
|
+
if isinstance(attribute, FancyArray):
|
49
|
+
yield attribute
|
50
|
+
|
51
|
+
@classmethod
|
52
|
+
def find_array_field(cls, array_type: Type[FancyArray]) -> dataclasses.Field:
|
53
|
+
"""Find the Field that holds an array of type array_type.
|
54
|
+
|
55
|
+
Args:
|
56
|
+
array_type(type[FancyArray]): FancyArray subclass.
|
57
|
+
|
58
|
+
Raises:
|
59
|
+
TypeError: if no field with the given `array_type` is found or if multiple fields are found.
|
60
|
+
|
61
|
+
Returns:
|
62
|
+
a Field instance.
|
63
|
+
"""
|
64
|
+
fields = [
|
65
|
+
field
|
66
|
+
for field in dataclasses.fields(cls)
|
67
|
+
if inspect.isclass(field.type) and issubclass(field.type, array_type)
|
68
|
+
]
|
69
|
+
if (nr_fields := len(fields)) != 1:
|
70
|
+
raise TypeError(
|
71
|
+
f"Expected to find 1 array with type '{array_type.__name__}' in {cls.__name__} ({nr_fields} found)"
|
72
|
+
)
|
73
|
+
return fields[0]
|
74
|
+
|
75
|
+
@property
|
76
|
+
def max_id(self) -> int:
|
77
|
+
"""Returns the max id across all arrays within the container."""
|
78
|
+
max_per_array = [np.max(array.id) if array.size > 0 else 0 for array in self.all_arrays()]
|
79
|
+
return int(max(max_per_array))
|
80
|
+
|
81
|
+
def check_ids(self, check_between_arrays: bool = True, check_within_arrays: bool = True) -> None:
|
82
|
+
"""Checks for duplicate id values across all arrays in the container.
|
83
|
+
|
84
|
+
Args:
|
85
|
+
check_between_arrays(bool): whether to check for duplicate ids across arrays
|
86
|
+
check_within_arrays(bool): whether to check for duplicate ids within each array
|
87
|
+
|
88
|
+
Raises:
|
89
|
+
ValueError: if duplicates are found.
|
90
|
+
"""
|
91
|
+
|
92
|
+
id_arrays = [array for array in self.all_arrays() if hasattr(array, "id")]
|
93
|
+
if not id_arrays:
|
94
|
+
return # no arrays to check
|
95
|
+
|
96
|
+
duplicates_between_arrays = self._get_duplicates_between_arrays(
|
97
|
+
id_arrays, check=check_between_arrays
|
98
|
+
) # if check_between_arrays else []
|
99
|
+
arrays_with_duplicates = self._get_arrays_with_duplicates(id_arrays, check=check_within_arrays)
|
100
|
+
|
101
|
+
if not any(duplicates_between_arrays) and not any(arrays_with_duplicates):
|
102
|
+
return
|
103
|
+
|
104
|
+
if any(duplicates_between_arrays):
|
105
|
+
logging.warning(f"The following ids occur in multiple arrays: {duplicates_between_arrays}!")
|
106
|
+
for array_class in arrays_with_duplicates:
|
107
|
+
logging.warning(f"{array_class.__name__} contains duplicates!")
|
108
|
+
|
109
|
+
raise ValueError(f"Duplicates found within {self.__class__.__name__}!")
|
110
|
+
|
111
|
+
def append(self, array: FancyArray, check_max_id: bool = True) -> None:
|
112
|
+
"""Append the given asset_array to the corresponding field of ArrayContainer and generate ids.
|
113
|
+
|
114
|
+
Args:
|
115
|
+
array(FancyArray): the asset_array to be appended (e.g. a NodeArray instance).
|
116
|
+
check_max_id(bool): whether to check max(array.id) with the id counter
|
117
|
+
|
118
|
+
Returns:
|
119
|
+
None
|
120
|
+
"""
|
121
|
+
self._append(array=array, check_max_id=check_max_id)
|
122
|
+
|
123
|
+
def attach_ids(self, array: FancyArray) -> FancyArray:
|
124
|
+
"""Generate and attach ids to the given FancyArray. Also updates _id_counter.
|
125
|
+
|
126
|
+
Args:
|
127
|
+
array(FancyArray): the array of which the id column is set.
|
128
|
+
|
129
|
+
Returns:
|
130
|
+
FancyArray: initial array with updated `id` column.
|
131
|
+
"""
|
132
|
+
if not array.size:
|
133
|
+
return array
|
134
|
+
|
135
|
+
if (id_set := set(array.id)) != {array.get_empty_value("id")}:
|
136
|
+
raise ValueError(f"Cannot attach ids to array that contains non-empty ids: {id_set}")
|
137
|
+
|
138
|
+
start = self._id_counter + 1
|
139
|
+
end = start + len(array)
|
140
|
+
array.id = np.arange(start, end)
|
141
|
+
self._id_counter = max(self._id_counter, end - 1)
|
142
|
+
|
143
|
+
return array
|
144
|
+
|
145
|
+
def search_for_id(self, record_id: int) -> list[FancyArray]:
|
146
|
+
"""Attempts to find a record across all id-arrays within the container.
|
147
|
+
|
148
|
+
This method is only intended for debugging purposes since it is very inefficient.
|
149
|
+
In normal circumstances you should use ``get`` or ``filter`` to find records within a specific array.
|
150
|
+
|
151
|
+
Args:
|
152
|
+
record_id(int): the id of the record to be found.
|
153
|
+
|
154
|
+
Returns:
|
155
|
+
list[FancyArray]:a list of arrays that contain the given record_id.
|
156
|
+
Each array within the list contains all records with the given array.
|
157
|
+
"""
|
158
|
+
|
159
|
+
logging.warning("Using search_for_id(). Make sure to use only while debugging!")
|
160
|
+
|
161
|
+
arrays_with_record = []
|
162
|
+
|
163
|
+
id_arrays = [array for array in self.all_arrays() if "id" in array.dtype.names]
|
164
|
+
for id_array in id_arrays:
|
165
|
+
matching_records = id_array.filter(id=record_id)
|
166
|
+
if matching_records.size:
|
167
|
+
arrays_with_record.append(matching_records)
|
168
|
+
|
169
|
+
if arrays_with_record:
|
170
|
+
return arrays_with_record
|
171
|
+
raise RecordDoesNotExist(f"record id '{record_id}' not found in {self.__class__.__name__}")
|
172
|
+
|
173
|
+
def _append(self, array: FancyArray, check_max_id: bool = True) -> None:
|
174
|
+
"""
|
175
|
+
Append the given asset_array to the corresponding field of Grid and generate ids.
|
176
|
+
Args:
|
177
|
+
array: the asset_array to be appended (e.g. a KabelArray instance).
|
178
|
+
check_max_id: whether to check max(array.id) with the id counter
|
179
|
+
Returns: None.
|
180
|
+
"""
|
181
|
+
if array.size == 0:
|
182
|
+
return
|
183
|
+
|
184
|
+
array_field = self.find_array_field(array.__class__)
|
185
|
+
|
186
|
+
if hasattr(array, "id"):
|
187
|
+
self._update_id_counter(array, check_max_id)
|
188
|
+
|
189
|
+
# Add the given asset_array to the corresponding array in the Grid.
|
190
|
+
array_attr = getattr(self, array_field.name)
|
191
|
+
appended = fp.concatenate(array_attr, array)
|
192
|
+
setattr(self, array_field.name, appended)
|
193
|
+
|
194
|
+
@classmethod
|
195
|
+
def _get_empty_fields(cls) -> dict:
|
196
|
+
empty_fields = {}
|
197
|
+
|
198
|
+
empty_fields.update(cls._get_empty_arrays())
|
199
|
+
empty_fields.update({"_id_counter": 0})
|
200
|
+
return empty_fields
|
201
|
+
|
202
|
+
@classmethod
|
203
|
+
def _get_empty_arrays(cls) -> dict:
|
204
|
+
return {
|
205
|
+
field.name: field.type()
|
206
|
+
for field in dataclasses.fields(cls)
|
207
|
+
if inspect.isclass(field.type) and issubclass(field.type, FancyArray)
|
208
|
+
}
|
209
|
+
|
210
|
+
def _update_id_counter(self, array, check_max_id: bool = True):
|
211
|
+
if np.all(array.id == EMPTY_ID):
|
212
|
+
array = self.attach_ids(array)
|
213
|
+
elif np.any(array.id == EMPTY_ID):
|
214
|
+
raise ValueError(f"Cannot append: array contains empty [{EMPTY_ID}] and non-empty ids.")
|
215
|
+
|
216
|
+
new_max_id = np.max(array.id)
|
217
|
+
if check_max_id and new_max_id < self._id_counter:
|
218
|
+
raise ValueError(f"Cannot append: id {new_max_id} is lower than the id counter")
|
219
|
+
|
220
|
+
# Update _id_counter
|
221
|
+
self._id_counter = max(self._id_counter, new_max_id)
|
222
|
+
|
223
|
+
@staticmethod
|
224
|
+
def _get_duplicates_between_arrays(id_arrays: list[FancyArray], check: bool) -> np.ndarray:
|
225
|
+
if not check:
|
226
|
+
return np.array([])
|
227
|
+
unique_ids_per_array = [np.unique(array.id) for array in id_arrays]
|
228
|
+
|
229
|
+
all_ids = np.concatenate(unique_ids_per_array)
|
230
|
+
|
231
|
+
unique_ids, counts = np.unique(all_ids, return_counts=True)
|
232
|
+
duplicate_mask = counts > 1
|
233
|
+
return unique_ids[duplicate_mask]
|
234
|
+
|
235
|
+
@staticmethod
|
236
|
+
def _get_arrays_with_duplicates(id_arrays: list[FancyArray], check: bool) -> list:
|
237
|
+
arrays_with_duplicates: list[Type] = []
|
238
|
+
if not check:
|
239
|
+
return arrays_with_duplicates
|
240
|
+
for id_array in id_arrays:
|
241
|
+
duplicates: np.ndarray = id_array.check_ids(return_duplicates=True)
|
242
|
+
if duplicates.size > 0:
|
243
|
+
arrays_with_duplicates.append(id_arrays.__class__)
|
244
|
+
return arrays_with_duplicates
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
|
2
|
+
#
|
3
|
+
# SPDX-License-Identifier: MPL-2.0
|
4
|
+
"""This file contains the Grid protocol defining the minimal arrays contained in a grid"""
|
5
|
+
|
6
|
+
from typing import Protocol
|
7
|
+
|
8
|
+
from power_grid_model_ds._core.model.arrays import (
|
9
|
+
BranchArray,
|
10
|
+
NodeArray,
|
11
|
+
ThreeWindingTransformerArray,
|
12
|
+
)
|
13
|
+
|
14
|
+
|
15
|
+
class MinimalGridArrays(Protocol):
|
16
|
+
"""Protocol for the minimal arrays contained in a grid,
|
17
|
+
they may be implemented using properties or added as attributes"""
|
18
|
+
|
19
|
+
node: NodeArray
|
20
|
+
three_winding_transformer: ThreeWindingTransformerArray
|
21
|
+
branches: BranchArray
|
22
|
+
branch_arrays: list[BranchArray]
|
File without changes
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
|
2
|
+
#
|
3
|
+
# SPDX-License-Identifier: MPL-2.0
|
4
|
+
|
5
|
+
"""Appliance data types"""
|
6
|
+
|
7
|
+
import numpy as np
|
8
|
+
from numpy.typing import NDArray
|
9
|
+
|
10
|
+
from power_grid_model_ds._core.model.dtypes.id import Id
|
11
|
+
|
12
|
+
|
13
|
+
class Appliance(Id):
|
14
|
+
"""Appliance data type"""
|
15
|
+
|
16
|
+
node: NDArray[np.int32] # id of the coupled node
|
17
|
+
status: NDArray[np.int8] # connection status to the node
|
18
|
+
|
19
|
+
|
20
|
+
class Source(Appliance):
|
21
|
+
"""Source data type"""
|
22
|
+
|
23
|
+
u_ref: NDArray[np.float64] # reference voltage
|
24
|
+
|
25
|
+
|
26
|
+
class SymLoad(Appliance):
|
27
|
+
"""SymLoad data type"""
|
28
|
+
|
29
|
+
type: NDArray[np.int8] # load type
|
30
|
+
p_specified: NDArray[np.float64] # specified active power
|
31
|
+
q_specified: NDArray[np.float64] # specified reactive power
|
32
|
+
|
33
|
+
|
34
|
+
class SymGen(Appliance):
|
35
|
+
"""SymGen data type"""
|
36
|
+
|
37
|
+
type: NDArray[np.int_] # load type
|
38
|
+
p_specified: NDArray[np.float64] # specified active power
|
39
|
+
q_specified: NDArray[np.float64] # specified reactive power
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
|
2
|
+
#
|
3
|
+
# SPDX-License-Identifier: MPL-2.0
|
4
|
+
|
5
|
+
"""Branch data types"""
|
6
|
+
|
7
|
+
import numpy as np
|
8
|
+
from numpy.typing import NDArray
|
9
|
+
|
10
|
+
from power_grid_model_ds._core.model.constants import empty
|
11
|
+
from power_grid_model_ds._core.model.dtypes.id import Id
|
12
|
+
|
13
|
+
|
14
|
+
class Branch(Id):
|
15
|
+
"""Branch data type"""
|
16
|
+
|
17
|
+
from_node: NDArray[np.int32] # node id (from-side)
|
18
|
+
to_node: NDArray[np.int32] # node id (to-side)
|
19
|
+
from_status: NDArray[np.int8] # 1 = closed, 0 = open
|
20
|
+
to_status: NDArray[np.int8] # 1 = closed, 0 = open
|
21
|
+
feeder_branch_id: NDArray[np.int32] # branch id of the feeding branch
|
22
|
+
feeder_node_id: NDArray[np.int32] # node id of the feeding node
|
23
|
+
is_feeder: NDArray[np.bool_] # whether or not this branch is from the substation
|
24
|
+
|
25
|
+
_defaults = {
|
26
|
+
"feeder_branch_id": empty,
|
27
|
+
"feeder_node_id": empty,
|
28
|
+
"is_feeder": False,
|
29
|
+
}
|
30
|
+
|
31
|
+
|
32
|
+
class Link(Branch):
|
33
|
+
"""Link data type"""
|
34
|
+
|
35
|
+
|
36
|
+
class Line(Branch):
|
37
|
+
"""Line data type"""
|
38
|
+
|
39
|
+
r1: NDArray[np.float64] # serial resistance
|
40
|
+
x1: NDArray[np.float64] # serial reactance
|
41
|
+
c1: NDArray[np.float64] # shunt capacitance
|
42
|
+
tan1: NDArray[np.float64] # shunt loss factor
|
43
|
+
i_n: NDArray[np.float64] # rated current
|
44
|
+
|
45
|
+
|
46
|
+
class Transformer(Branch):
|
47
|
+
"""Transformer data type"""
|
48
|
+
|
49
|
+
u1: NDArray[np.float64] # rated voltage (from-side)
|
50
|
+
u2: NDArray[np.float64] # rated voltage (to-side)
|
51
|
+
sn: NDArray[np.float64] # rated power
|
52
|
+
tap_size: NDArray[np.float64] # size of each tap of the tap changer
|
53
|
+
uk: NDArray[np.float64] # relative short circuit voltage
|
54
|
+
pk: NDArray[np.float64] # short circuit loss
|
55
|
+
i0: NDArray[np.float64] # relative no-load current
|
56
|
+
p0: NDArray[np.float64] # no-load loss
|
57
|
+
winding_from: NDArray[np.int8] # winding type (from-side)
|
58
|
+
winding_to: NDArray[np.int8] # winding type (to-side)
|
59
|
+
clock: NDArray[np.int8] # clock number of phase shift
|
60
|
+
tap_side: NDArray[np.int8] # side of tap changer
|
61
|
+
tap_pos: NDArray[np.int8] # current position of tap changer
|
62
|
+
tap_min: NDArray[np.int8] # position of tap changer at minimum voltage
|
63
|
+
tap_max: NDArray[np.int8] # position of tap changer at maximum voltage
|
64
|
+
tap_nom: NDArray[np.int8] # nominal position of tap changer
|
65
|
+
|
66
|
+
|
67
|
+
class Branch3(Id):
|
68
|
+
"""Branch3 data type"""
|
69
|
+
|
70
|
+
node_1: NDArray[np.int32]
|
71
|
+
node_2: NDArray[np.int32]
|
72
|
+
node_3: NDArray[np.int32]
|
73
|
+
status_1: NDArray[np.int8]
|
74
|
+
status_2: NDArray[np.int8]
|
75
|
+
status_3: NDArray[np.int8]
|
76
|
+
|
77
|
+
|
78
|
+
class ThreeWindingTransformer(Branch3):
|
79
|
+
"""ThreeWindingTransformer data type"""
|
80
|
+
|
81
|
+
u1: NDArray[np.float64]
|
82
|
+
u2: NDArray[np.float64]
|
83
|
+
u3: NDArray[np.float64]
|
84
|
+
sn_1: NDArray[np.float64]
|
85
|
+
sn_2: NDArray[np.float64]
|
86
|
+
sn_3: NDArray[np.float64]
|
87
|
+
uk_12: NDArray[np.float64]
|
88
|
+
uk_13: NDArray[np.float64]
|
89
|
+
uk_23: NDArray[np.float64]
|
90
|
+
pk_12: NDArray[np.float64]
|
91
|
+
pk_13: NDArray[np.float64]
|
92
|
+
pk_23: NDArray[np.float64]
|
93
|
+
i0: NDArray[np.float64]
|
94
|
+
p0: NDArray[np.float64]
|
95
|
+
winding_1: NDArray[np.int8]
|
96
|
+
winding_2: NDArray[np.int8]
|
97
|
+
winding_3: NDArray[np.int8]
|
98
|
+
clock_12: NDArray[np.int8]
|
99
|
+
clock_13: NDArray[np.int8]
|
100
|
+
tap_side: NDArray[np.int8]
|
101
|
+
tap_pos: NDArray[np.int8]
|
102
|
+
tap_min: NDArray[np.int8]
|
103
|
+
tap_max: NDArray[np.int8]
|
104
|
+
tap_nom: NDArray[np.int8]
|
105
|
+
tap_size: NDArray[np.float64]
|
106
|
+
uk_12_min: NDArray[np.float64]
|
107
|
+
uk_13_min: NDArray[np.float64]
|
108
|
+
uk_23_min: NDArray[np.float64]
|
109
|
+
pk_12_min: NDArray[np.float64]
|
110
|
+
pk_13_min: NDArray[np.float64]
|
111
|
+
pk_23_min: NDArray[np.float64]
|
112
|
+
uk_12_max: NDArray[np.float64]
|
113
|
+
uk_13_max: NDArray[np.float64]
|
114
|
+
uk_23_max: NDArray[np.float64]
|
115
|
+
pk_12_max: NDArray[np.float64]
|
116
|
+
pk_13_max: NDArray[np.float64]
|
117
|
+
pk_23_max: NDArray[np.float64]
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
|
2
|
+
#
|
3
|
+
# SPDX-License-Identifier: MPL-2.0
|
4
|
+
|
5
|
+
"""Base data types"""
|
6
|
+
|
7
|
+
from typing import Any
|
8
|
+
|
9
|
+
import numpy as np
|
10
|
+
from numpy.typing import NDArray
|
11
|
+
|
12
|
+
from power_grid_model_ds._core.model.constants import empty
|
13
|
+
|
14
|
+
|
15
|
+
class Id:
|
16
|
+
"""Base dtype for id arrays"""
|
17
|
+
|
18
|
+
_defaults: dict[str, Any] = {"id": empty}
|
19
|
+
id: NDArray[np.int32]
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
|
2
|
+
#
|
3
|
+
# SPDX-License-Identifier: MPL-2.0
|
4
|
+
|
5
|
+
"""Node data types"""
|
6
|
+
|
7
|
+
import numpy as np
|
8
|
+
from numpy.typing import NDArray
|
9
|
+
|
10
|
+
from power_grid_model_ds._core.model.constants import empty
|
11
|
+
from power_grid_model_ds._core.model.dtypes.id import Id
|
12
|
+
from power_grid_model_ds._core.model.enums.nodes import NodeType
|
13
|
+
|
14
|
+
|
15
|
+
class Node(Id):
|
16
|
+
"""Node data type"""
|
17
|
+
|
18
|
+
u_rated: NDArray[np.float64] # rated line-line voltage
|
19
|
+
node_type: NDArray[np.int8]
|
20
|
+
feeder_branch_id: NDArray[np.int32] # branch id of the feeder
|
21
|
+
feeder_node_id: NDArray[np.int32] # node id of the first substation node
|
22
|
+
|
23
|
+
_defaults = {
|
24
|
+
"node_type": NodeType.UNSPECIFIED.value,
|
25
|
+
"feeder_branch_id": empty,
|
26
|
+
"feeder_node_id": empty,
|
27
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
|
2
|
+
#
|
3
|
+
# SPDX-License-Identifier: MPL-2.0
|
4
|
+
|
5
|
+
import numpy as np
|
6
|
+
from numpy.typing import NDArray
|
7
|
+
|
8
|
+
from power_grid_model_ds._core.model.dtypes.id import Id
|
9
|
+
|
10
|
+
|
11
|
+
class Regulator(Id):
|
12
|
+
"Regulator data type"
|
13
|
+
|
14
|
+
regulated_object: NDArray[np.int32] # a valid regulated object ID
|
15
|
+
status: NDArray[np.int8] # connection status of regulated object
|
16
|
+
|
17
|
+
|
18
|
+
class TransformerTapRegulator(Regulator):
|
19
|
+
"""Transformer tap regulator data type"""
|
20
|
+
|
21
|
+
control_side: NDArray[np.int8] # the controlled side of the transformer (see BranchSide/Branch3Side of PGM)
|
22
|
+
u_set: NDArray[np.float64] # the voltage setpoint
|
23
|
+
u_band: NDArray[np.float64] # the width of the voltage band
|
24
|
+
line_drop_compensation_r: NDArray[np.float64] # compensation for voltage drop due to resistance during transport
|
25
|
+
line_drop_compensation_x: NDArray[np.float64] # compensation for voltage drop due to reactance during transport
|
26
|
+
|
27
|
+
_defaults = {
|
28
|
+
"line_drop_compensation_r": 0,
|
29
|
+
"line_drop_compensation_x": 0,
|
30
|
+
}
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
|
2
|
+
#
|
3
|
+
# SPDX-License-Identifier: MPL-2.0
|
4
|
+
|
5
|
+
"""
|
6
|
+
Sensor data types
|
7
|
+
Based on the sensors defined in power grid model:
|
8
|
+
https://power-grid-model.readthedocs.io/en/v1.9.13/user_manual/components.html#sensor
|
9
|
+
"""
|
10
|
+
|
11
|
+
from typing import Annotated, Literal, TypeVar
|
12
|
+
|
13
|
+
import numpy as np
|
14
|
+
from numpy.typing import NDArray
|
15
|
+
|
16
|
+
from power_grid_model_ds._core.model.dtypes.id import Id
|
17
|
+
|
18
|
+
# define structural arrays with 3 values for 3-phase variables
|
19
|
+
# based on https://stackoverflow.com/a/72585748
|
20
|
+
_DT = TypeVar("_DT", bound=np.generic)
|
21
|
+
NDArray3 = Annotated[NDArray[_DT], Literal[3]]
|
22
|
+
|
23
|
+
|
24
|
+
class Sensor(Id):
|
25
|
+
"""Base class for sensor data type"""
|
26
|
+
|
27
|
+
measured_object: NDArray[np.int32]
|
28
|
+
|
29
|
+
|
30
|
+
class GenericPowerSensor(Sensor):
|
31
|
+
"""Base class for power sensor data type"""
|
32
|
+
|
33
|
+
measured_terminal_type: NDArray[np.int32]
|
34
|
+
power_sigma: NDArray[np.float64] # std of total power
|
35
|
+
|
36
|
+
|
37
|
+
class SymPowerSensor(GenericPowerSensor):
|
38
|
+
"""SymPowerSensor data type"""
|
39
|
+
|
40
|
+
p_measured: NDArray[np.float64] # measured active power
|
41
|
+
q_measured: NDArray[np.float64] # measured reactive power
|
42
|
+
p_sigma: NDArray[np.float64] # std of active power
|
43
|
+
q_sigma: NDArray[np.float64] # std of reactive power
|
44
|
+
|
45
|
+
|
46
|
+
class GenericVoltageSensor(Sensor):
|
47
|
+
"""Base class for voltage sensor data type"""
|
48
|
+
|
49
|
+
|
50
|
+
class SymVoltageSensor(GenericVoltageSensor):
|
51
|
+
"""SymVoltageSensor data type"""
|
52
|
+
|
53
|
+
u_sigma: NDArray[np.float64] # std of voltage
|
54
|
+
u_measured: NDArray[np.float64] # measured voltage
|
55
|
+
u_angle_measured: NDArray[np.float64] # measured phase
|
56
|
+
|
57
|
+
|
58
|
+
class AsymVoltageSensor(GenericVoltageSensor):
|
59
|
+
"""AsymVoltageSensor data type"""
|
60
|
+
|
61
|
+
u_sigma: NDArray3[np.float64] # std of 3 voltages
|
62
|
+
u_measured: NDArray3[np.float64] # measured 3 voltages
|
63
|
+
u_angle_measured: NDArray3[np.float64] # measured 3 phases
|
File without changes
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
|
2
|
+
#
|
3
|
+
# SPDX-License-Identifier: MPL-2.0
|
4
|
+
|
5
|
+
"""Enums for Nodes"""
|
6
|
+
|
7
|
+
from enum import IntEnum
|
8
|
+
|
9
|
+
|
10
|
+
class NodeType(IntEnum):
|
11
|
+
"""Node Types
|
12
|
+
Nodes located within a substation, are marked as Substation nodes.
|
13
|
+
"""
|
14
|
+
|
15
|
+
UNSPECIFIED = 0
|
16
|
+
SUBSTATION_NODE = 1
|
File without changes
|