power-grid-model 1.10.74__py3-none-win_amd64.whl → 1.12.119__py3-none-win_amd64.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.

Potentially problematic release.


This version of power-grid-model might be problematic. Click here for more details.

Files changed (67) hide show
  1. power_grid_model/__init__.py +54 -29
  2. power_grid_model/_core/__init__.py +3 -3
  3. power_grid_model/_core/buffer_handling.py +507 -478
  4. power_grid_model/_core/data_handling.py +195 -141
  5. power_grid_model/_core/data_types.py +142 -0
  6. power_grid_model/_core/dataset_definitions.py +109 -109
  7. power_grid_model/_core/enum.py +226 -0
  8. power_grid_model/_core/error_handling.py +215 -202
  9. power_grid_model/_core/errors.py +134 -0
  10. power_grid_model/_core/index_integer.py +17 -17
  11. power_grid_model/_core/options.py +71 -69
  12. power_grid_model/_core/power_grid_core.py +577 -597
  13. power_grid_model/_core/power_grid_dataset.py +545 -528
  14. power_grid_model/_core/power_grid_meta.py +262 -244
  15. power_grid_model/_core/power_grid_model.py +1025 -692
  16. power_grid_model/_core/power_grid_model_c/__init__.py +3 -0
  17. power_grid_model/_core/power_grid_model_c/bin/power_grid_model_c.dll +0 -0
  18. power_grid_model/_core/power_grid_model_c/get_pgm_dll_path.py +63 -0
  19. power_grid_model/_core/power_grid_model_c/include/power_grid_model_c/basics.h +251 -0
  20. power_grid_model/_core/power_grid_model_c/include/power_grid_model_c/buffer.h +108 -0
  21. power_grid_model/_core/power_grid_model_c/include/power_grid_model_c/dataset.h +332 -0
  22. power_grid_model/_core/power_grid_model_c/include/power_grid_model_c/dataset_definitions.h +1060 -0
  23. power_grid_model/_core/power_grid_model_c/include/power_grid_model_c/handle.h +111 -0
  24. power_grid_model/_core/power_grid_model_c/include/power_grid_model_c/meta_data.h +189 -0
  25. power_grid_model/_core/power_grid_model_c/include/power_grid_model_c/model.h +130 -0
  26. power_grid_model/_core/power_grid_model_c/include/power_grid_model_c/options.h +142 -0
  27. power_grid_model/_core/power_grid_model_c/include/power_grid_model_c/serialization.h +118 -0
  28. power_grid_model/_core/power_grid_model_c/include/power_grid_model_c.h +36 -0
  29. power_grid_model/_core/power_grid_model_c/include/power_grid_model_cpp/basics.hpp +65 -0
  30. power_grid_model/_core/power_grid_model_c/include/power_grid_model_cpp/buffer.hpp +61 -0
  31. power_grid_model/_core/power_grid_model_c/include/power_grid_model_cpp/dataset.hpp +224 -0
  32. power_grid_model/_core/power_grid_model_c/include/power_grid_model_cpp/handle.hpp +108 -0
  33. power_grid_model/_core/power_grid_model_c/include/power_grid_model_cpp/meta_data.hpp +84 -0
  34. power_grid_model/_core/power_grid_model_c/include/power_grid_model_cpp/model.hpp +63 -0
  35. power_grid_model/_core/power_grid_model_c/include/power_grid_model_cpp/options.hpp +52 -0
  36. power_grid_model/_core/power_grid_model_c/include/power_grid_model_cpp/serialization.hpp +124 -0
  37. power_grid_model/_core/power_grid_model_c/include/power_grid_model_cpp/utils.hpp +81 -0
  38. power_grid_model/_core/power_grid_model_c/include/power_grid_model_cpp.hpp +19 -0
  39. power_grid_model/_core/power_grid_model_c/lib/cmake/power_grid_model/power_grid_modelConfig.cmake +37 -0
  40. power_grid_model/_core/power_grid_model_c/lib/cmake/power_grid_model/power_grid_modelConfigVersion.cmake +65 -0
  41. power_grid_model/_core/power_grid_model_c/lib/cmake/power_grid_model/power_grid_modelTargets-release.cmake +19 -0
  42. power_grid_model/_core/power_grid_model_c/lib/cmake/power_grid_model/power_grid_modelTargets.cmake +144 -0
  43. power_grid_model/_core/power_grid_model_c/lib/power_grid_model_c.lib +0 -0
  44. power_grid_model/_core/power_grid_model_c/share/LICENSE +292 -0
  45. power_grid_model/_core/power_grid_model_c/share/README.md +15 -0
  46. power_grid_model/_core/serialization.py +319 -317
  47. power_grid_model/_core/typing.py +20 -0
  48. power_grid_model/{_utils.py → _core/utils.py} +798 -783
  49. power_grid_model/data_types.py +321 -319
  50. power_grid_model/enum.py +27 -214
  51. power_grid_model/errors.py +37 -123
  52. power_grid_model/typing.py +43 -48
  53. power_grid_model/utils.py +529 -400
  54. power_grid_model/validation/__init__.py +25 -14
  55. power_grid_model/validation/_rules.py +1167 -904
  56. power_grid_model/validation/_validation.py +1172 -980
  57. power_grid_model/validation/assertions.py +93 -92
  58. power_grid_model/validation/errors.py +602 -520
  59. power_grid_model/validation/utils.py +313 -318
  60. {power_grid_model-1.10.74.dist-info → power_grid_model-1.12.119.dist-info}/METADATA +162 -171
  61. power_grid_model-1.12.119.dist-info/RECORD +65 -0
  62. {power_grid_model-1.10.74.dist-info → power_grid_model-1.12.119.dist-info}/WHEEL +1 -1
  63. power_grid_model-1.12.119.dist-info/entry_points.txt +3 -0
  64. power_grid_model/_core/_power_grid_core.dll +0 -0
  65. power_grid_model-1.10.74.dist-info/RECORD +0 -32
  66. power_grid_model-1.10.74.dist-info/top_level.txt +0 -1
  67. {power_grid_model-1.10.74.dist-info → power_grid_model-1.12.119.dist-info}/licenses/LICENSE +0 -0
@@ -1,528 +1,545 @@
1
- # SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
2
- #
3
- # SPDX-License-Identifier: MPL-2.0
4
-
5
- """
6
- Power grid model raw dataset handler
7
- """
8
-
9
- from typing import Any, Mapping, cast
10
-
11
- from power_grid_model._core.buffer_handling import (
12
- BufferProperties,
13
- CAttributeBuffer,
14
- CBuffer,
15
- create_buffer,
16
- get_buffer_properties,
17
- get_buffer_view,
18
- )
19
- from power_grid_model._core.dataset_definitions import ComponentType, DatasetType, _str_to_component_type
20
- from power_grid_model._core.error_handling import VALIDATOR_MSG, assert_no_error
21
- from power_grid_model._core.power_grid_core import (
22
- ConstDatasetPtr,
23
- DatasetInfoPtr,
24
- MutableDatasetPtr,
25
- WritableDatasetPtr,
26
- power_grid_core as pgc,
27
- )
28
- from power_grid_model._core.power_grid_meta import ComponentMetaData, DatasetMetaData, power_grid_meta_data
29
- from power_grid_model._utils import get_dataset_type, is_columnar, is_nan_or_equivalent, is_sparse, process_data_filter
30
- from power_grid_model.data_types import (
31
- AttributeType,
32
- ColumnarData,
33
- ComponentData,
34
- Dataset,
35
- DenseBatchColumnarData,
36
- SingleColumnarData,
37
- SparseBatchColumnarData,
38
- )
39
- from power_grid_model.enum import ComponentAttributeFilterOptions
40
- from power_grid_model.typing import ComponentAttributeMapping, _ComponentAttributeMappingDict
41
-
42
-
43
- class CDatasetInfo: # pylint: disable=too-few-public-methods
44
- """
45
- Raw Power Grid Model dataset info.
46
- """
47
-
48
- def __init__(self, info: DatasetInfoPtr):
49
- self._info: DatasetInfoPtr = info
50
-
51
- def name(self) -> str:
52
- """
53
- The name of the dataset type.
54
-
55
- Returns:
56
- The name of the dataset type
57
- """
58
- return pgc.dataset_info_name(self._info)
59
-
60
- def dataset_type(self):
61
- """
62
- The name of the dataset type.
63
-
64
- Returns:
65
- The name of the dataset type
66
- """
67
- return self.name()
68
-
69
- def is_batch(self) -> bool:
70
- """
71
- Whether the dataset is a batch dataset.
72
-
73
- Returns:
74
- Whether the dataset is a batch dataset
75
- """
76
- return bool(pgc.dataset_info_is_batch(self._info))
77
-
78
- def batch_size(self) -> int:
79
- """
80
- The size of the dataset.
81
-
82
- Returns:
83
- The size of the dataset
84
- """
85
- return pgc.dataset_info_batch_size(self._info)
86
-
87
- def n_components(self) -> int:
88
- """
89
- The amount of components in the dataset.
90
-
91
- Returns:
92
- The amount of components in the dataset
93
- """
94
- return pgc.dataset_info_n_components(self._info)
95
-
96
- def components(self) -> list[ComponentType]:
97
- """
98
- The components in the dataset.
99
-
100
- Returns:
101
- A list of the component names in the dataset
102
- """
103
- return [
104
- _str_to_component_type(pgc.dataset_info_component_name(self._info, idx))
105
- for idx in range(self.n_components())
106
- ]
107
-
108
- def elements_per_scenario(self) -> Mapping[ComponentType, int]:
109
- """
110
- The number of elements per scenario in the dataset.
111
-
112
- Returns:
113
- The number of elements per senario for each component in the dataset;
114
- or -1 if the scenario is not uniform (different amount per scenario)
115
- """
116
- return {
117
- component_name: pgc.dataset_info_elements_per_scenario(self._info, idx)
118
- for idx, component_name in enumerate(self.components())
119
- }
120
-
121
- def total_elements(self) -> Mapping[ComponentType, int]:
122
- """
123
- The total number of elements in the dataset.
124
-
125
- Returns:
126
- The total number of elements for each component.
127
- For each component, if the number of elements per scenario is uniform, its value shall be equal to
128
- the product of the batch size and the amount of elements per scenario for that component.
129
- """
130
- return {
131
- component_name: pgc.dataset_info_total_elements(self._info, idx)
132
- for idx, component_name in enumerate(self.components())
133
- }
134
-
135
- def attribute_indications(self) -> Mapping[ComponentType, None | list[AttributeType]]:
136
- """
137
- The attribute indications in the dataset.
138
-
139
- Returns:
140
- A map of component to its attribute indications.
141
- None means no attribute indications
142
- """
143
- result_dict: dict[ComponentType, None | list[AttributeType]] = {}
144
- components = self.components()
145
- for component_idx, component_name in enumerate(components):
146
- has_indications = pgc.dataset_info_has_attribute_indications(self._info, component_idx)
147
- if has_indications == 0:
148
- result_dict[component_name] = None
149
- else:
150
- n_indications = pgc.dataset_info_n_attribute_indications(self._info, component_idx)
151
- result_dict[component_name] = [
152
- pgc.dataset_info_attribute_name(self._info, component_idx, attribute_idx)
153
- for attribute_idx in range(n_indications)
154
- ]
155
- return result_dict
156
-
157
-
158
- class CMutableDataset:
159
- """
160
- A view of a user-owned dataset.
161
-
162
- This may be used to provide a user dataset to the Power Grid Model.
163
-
164
- The dataset will create mutable buffers that the Power Grid Model can use to load data.
165
- """
166
-
167
- _dataset_type: DatasetType
168
- _schema: DatasetMetaData
169
- _is_batch: bool
170
- _batch_size: int
171
- _mutable_dataset: MutableDatasetPtr
172
- _buffer_views: list[CBuffer]
173
-
174
- def __new__(cls, data: Dataset, dataset_type: Any = None):
175
- instance = super().__new__(cls)
176
- instance._mutable_dataset = MutableDatasetPtr()
177
- instance._buffer_views = []
178
-
179
- instance._dataset_type = dataset_type if dataset_type in DatasetType else get_dataset_type(data)
180
- instance._schema = power_grid_meta_data[instance._dataset_type]
181
-
182
- if data:
183
- first_component, first_component_data = next(iter(data.items()))
184
- first_sub_info = get_buffer_properties(data=first_component_data, schema=instance._schema[first_component])
185
- instance._is_batch = first_sub_info.is_batch
186
- instance._batch_size = first_sub_info.batch_size
187
- else:
188
- instance._is_batch = False
189
- instance._batch_size = 1
190
-
191
- instance._mutable_dataset = pgc.create_dataset_mutable(
192
- instance._dataset_type.value, instance._is_batch, instance._batch_size
193
- )
194
- assert_no_error()
195
-
196
- instance._add_data(data)
197
- assert_no_error()
198
-
199
- return instance
200
-
201
- def get_dataset_ptr(self) -> MutableDatasetPtr:
202
- """
203
- Get the raw underlying mutable dataset pointer.
204
-
205
- Returns:
206
- MutableDatasetPtr: the raw underlying mutable dataset pointer.
207
- """
208
- return self._mutable_dataset
209
-
210
- def get_info(self) -> CDatasetInfo:
211
- """
212
- Get the info for this dataset.
213
-
214
- Returns:
215
- The dataset info for this dataset.
216
- """
217
- return CDatasetInfo(pgc.dataset_mutable_get_info(self._mutable_dataset))
218
-
219
- def get_buffer_views(self) -> list[CBuffer]:
220
- """
221
- Get list of buffer views
222
-
223
- Returns:
224
- list of buffer view
225
- """
226
- return self._buffer_views
227
-
228
- def _add_data(self, data: Dataset):
229
- """
230
- Add Power Grid Model data to the mutable dataset view.
231
-
232
- Args:
233
- data: the data.
234
-
235
- Raises:
236
- ValueError: if the component is unknown and allow_unknown is False.
237
- ValueError: if the data is inconsistent with the rest of the dataset.
238
- PowerGridError: if there was an internal error.
239
- """
240
- for component, component_data in data.items():
241
- self._add_component_data(component, component_data, allow_unknown=False)
242
-
243
- def _add_component_data(self, component: ComponentType, data: ComponentData, allow_unknown: bool = False):
244
- """
245
- Add Power Grid Model data for a single component to the mutable dataset view.
246
-
247
- Args:
248
- component: the name of the component
249
- data: the data of the component
250
- allow_unknown (optional): ignore any unknown components. Defaults to False.
251
-
252
- Raises:
253
- ValueError: if the component is unknown and allow_unknown is False.
254
- ValueError: if the data is inconsistent with the rest of the dataset.
255
- PowerGridError: if there was an internal error.
256
- """
257
- if component not in self._schema:
258
- if not allow_unknown:
259
- raise ValueError(f"Unknown component {component} in schema. {VALIDATOR_MSG}")
260
- return
261
-
262
- self._validate_properties(data, self._schema[component])
263
- c_buffer = get_buffer_view(data, self._schema[component], self._is_batch, self._batch_size)
264
- self._buffer_views.append(c_buffer)
265
- self._register_buffer(component, c_buffer)
266
-
267
- def _register_buffer(self, component: ComponentType, buffer: CBuffer):
268
- pgc.dataset_mutable_add_buffer(
269
- dataset=self._mutable_dataset,
270
- component=component.value,
271
- elements_per_scenario=buffer.n_elements_per_scenario,
272
- total_elements=buffer.total_elements,
273
- indptr=buffer.indptr,
274
- data=buffer.data,
275
- )
276
- assert_no_error()
277
- for attr, attr_data in buffer.attribute_data.items():
278
- self._register_attribute_buffer(component, attr, attr_data)
279
-
280
- def _register_attribute_buffer(self, component, attr, attr_data):
281
- pgc.dataset_mutable_add_attribute_buffer(
282
- dataset=self._mutable_dataset,
283
- component=component.value,
284
- attribute=attr,
285
- data=attr_data.data,
286
- )
287
- assert_no_error()
288
-
289
- def _validate_properties(self, data: ComponentData, schema: ComponentMetaData):
290
- properties = get_buffer_properties(data, schema=schema, is_batch=self._is_batch, batch_size=self._batch_size)
291
- if properties.is_batch != self._is_batch:
292
- raise ValueError(
293
- f"Dataset type (single or batch) must be consistent across all components. {VALIDATOR_MSG}"
294
- )
295
- if properties.batch_size != self._batch_size:
296
- raise ValueError(f"Dataset must have a consistent batch size across all components. {VALIDATOR_MSG}")
297
-
298
- def __del__(self):
299
- pgc.destroy_dataset_mutable(self._mutable_dataset)
300
-
301
-
302
- class CConstDataset:
303
- """
304
- A view of a user-owned dataset.
305
-
306
- This may be used to provide a user dataset to the Power Grid Model.
307
-
308
- The dataset will create const buffers that the Power Grid Model can use to load data.
309
-
310
- It is created from mutable dataset.
311
- """
312
-
313
- _const_dataset: ConstDatasetPtr
314
- _buffer_views: list[CBuffer]
315
-
316
- def __new__(cls, data: Dataset, dataset_type: DatasetType | None = None):
317
- instance = super().__new__(cls)
318
- instance._const_dataset = ConstDatasetPtr()
319
-
320
- # create from mutable dataset
321
- mutable_dataset = CMutableDataset(data=data, dataset_type=dataset_type)
322
- instance._const_dataset = pgc.create_dataset_const_from_mutable(mutable_dataset.get_dataset_ptr())
323
- assert_no_error()
324
- instance._buffer_views = mutable_dataset.get_buffer_views()
325
-
326
- return instance
327
-
328
- def get_dataset_ptr(self) -> ConstDatasetPtr:
329
- """
330
- Get the raw underlying const dataset pointer.
331
-
332
- Returns:
333
- ConstDatasetPtr: the raw underlying const dataset pointer.
334
- """
335
- return self._const_dataset
336
-
337
- def get_info(self) -> CDatasetInfo:
338
- """
339
- Get the info for this dataset.
340
-
341
- Returns:
342
- The dataset info for this dataset.
343
- """
344
- return CDatasetInfo(pgc.dataset_const_get_info(self._const_dataset))
345
-
346
- def __del__(self):
347
- pgc.destroy_dataset_const(self._const_dataset)
348
-
349
-
350
- class CWritableDataset:
351
- """
352
- A view of a Power Grid Model-owned dataset.
353
-
354
- This may be used to retrieve data from the Power Grid Model.
355
-
356
- This class provides buffers to which the Power Grid Model can write data in an external call.
357
- After writing to the buffers, the data contents can be retrieved.
358
- """
359
-
360
- def __init__(self, dataset_ptr: WritableDatasetPtr, data_filter: ComponentAttributeMapping):
361
- self._writable_dataset = dataset_ptr
362
-
363
- info = self.get_info()
364
- self._dataset_type = info.dataset_type()
365
- self._schema = power_grid_meta_data[self._dataset_type]
366
-
367
- self._data_filter = process_data_filter(
368
- dataset_type=info.dataset_type(),
369
- data_filter=data_filter,
370
- available_components=info.components(),
371
- )
372
-
373
- self._data: Dataset = {}
374
- self._buffers: Mapping[str, CBuffer] = {}
375
- self._component_buffer_properties = self._get_buffer_properties(info)
376
-
377
- self._add_buffers()
378
- assert_no_error()
379
-
380
- def get_dataset_ptr(self) -> WritableDatasetPtr:
381
- """
382
- Get the raw underlying writable dataset pointer.
383
-
384
- Returns:
385
- WritableDatasetPtr: the raw underlying writable dataset pointer.
386
- """
387
- return self._writable_dataset
388
-
389
- def get_info(self) -> CDatasetInfo:
390
- """
391
- Get the info for this dataset.
392
-
393
- Returns:
394
- The dataset info for this dataset.
395
- """
396
- return CDatasetInfo(pgc.dataset_writable_get_info(self._writable_dataset))
397
-
398
- def get_data(self) -> Dataset:
399
- """
400
- Retrieve data from the Power Grid Model dataset.
401
-
402
- The Power Grid Model may write to these buffers at a later point in time.
403
-
404
- Returns:
405
- The full dataset with filters applied.
406
- """
407
- self._post_filtering()
408
- return self._data
409
-
410
- def get_component_data(self, component: ComponentType) -> ComponentData:
411
- """
412
- Retrieve Power Grid Model data from the dataset for a specific component.
413
-
414
- Args:
415
- component: the component.
416
-
417
- Returns:
418
- The dataset for the specified component.
419
- """
420
- return self._data[component]
421
-
422
- def get_data_filter(self) -> _ComponentAttributeMappingDict:
423
- """Gets the data filter requested
424
-
425
- Returns:
426
- _ComponentAttributeMappingDict: data filter
427
- """
428
- return self._data_filter
429
-
430
- def _add_buffers(self):
431
- for component, buffer_properties in self._component_buffer_properties.items():
432
- self._add_buffer(component, buffer_properties)
433
-
434
- def _add_buffer(self, component: ComponentType, buffer_properties: BufferProperties):
435
- schema = self._schema[component]
436
-
437
- self._data[component] = create_buffer(buffer_properties, schema)
438
- self._register_buffer(component, get_buffer_view(self._data[component], schema))
439
-
440
- def _register_buffer(self, component: ComponentType, buffer: CBuffer):
441
- pgc.dataset_writable_set_buffer(
442
- dataset=self._writable_dataset,
443
- component=component,
444
- indptr=buffer.indptr,
445
- data=buffer.data,
446
- )
447
- assert_no_error()
448
- for attribute, attribute_data in buffer.attribute_data.items():
449
- self._register_attribute_buffer(component, attribute, attribute_data)
450
-
451
- def _register_attribute_buffer(
452
- self,
453
- component: ComponentType,
454
- attribute: AttributeType,
455
- buffer: CAttributeBuffer,
456
- ):
457
- pgc.dataset_writable_set_attribute_buffer(
458
- dataset=self._writable_dataset,
459
- component=component,
460
- attribute=attribute,
461
- data=buffer.data,
462
- )
463
- assert_no_error()
464
-
465
- def _get_buffer_properties(self, info: CDatasetInfo) -> Mapping[ComponentType, BufferProperties]:
466
- is_batch = info.is_batch()
467
- batch_size = info.batch_size()
468
- components = info.components()
469
- n_elements_per_scenario = info.elements_per_scenario()
470
- n_total_elements = info.total_elements()
471
- attribute_indications = info.attribute_indications()
472
-
473
- return {
474
- component: BufferProperties(
475
- is_sparse=n_elements_per_scenario[component] == -1,
476
- is_batch=is_batch,
477
- batch_size=batch_size,
478
- n_elements_per_scenario=n_elements_per_scenario[component],
479
- n_total_elements=n_total_elements[component],
480
- columns=_get_filtered_attributes(
481
- schema=self._schema[component],
482
- component_data_filter=self._data_filter[component],
483
- attribute_indication=attribute_indications[component],
484
- ),
485
- )
486
- for component in components
487
- if component in self._data_filter
488
- }
489
-
490
- def _filter_attributes(self, buffer: ColumnarData):
491
- if is_sparse(buffer):
492
- attributes = cast(SparseBatchColumnarData, buffer)["data"]
493
- else:
494
- attributes = cast(SingleColumnarData | DenseBatchColumnarData, buffer)
495
-
496
- keys_to_remove = []
497
- for attr, array in attributes.items():
498
- if is_nan_or_equivalent(array):
499
- keys_to_remove.append(attr)
500
- for key in keys_to_remove:
501
- del attributes[key]
502
-
503
- def _filter_with_mapping(self):
504
- for component_type, component_buffer in self._data.items():
505
- if component_type in self._data_filter:
506
- filter_option = self._data_filter[component_type]
507
- if filter_option is ComponentAttributeFilterOptions.relevant and is_columnar(component_buffer):
508
- self._filter_attributes(component_buffer)
509
-
510
- def _post_filtering(self):
511
- if isinstance(self._data_filter, dict):
512
- self._filter_with_mapping()
513
-
514
-
515
- def _get_filtered_attributes(
516
- schema: ComponentMetaData,
517
- component_data_filter: set[str] | list[str] | None | ComponentAttributeFilterOptions,
518
- attribute_indication: None | list[AttributeType],
519
- ) -> list[AttributeType] | None:
520
- if component_data_filter is None:
521
- return None
522
-
523
- if isinstance(component_data_filter, ComponentAttributeFilterOptions):
524
- if component_data_filter == ComponentAttributeFilterOptions.relevant and attribute_indication is not None:
525
- return attribute_indication
526
- return [] if schema.dtype.names is None else list(schema.dtype.names)
527
-
528
- return list(component_data_filter)
1
+ # SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
2
+ #
3
+ # SPDX-License-Identifier: MPL-2.0
4
+
5
+ """
6
+ Power grid model raw dataset handler
7
+ """
8
+
9
+ from collections.abc import Mapping
10
+ from typing import Any, cast
11
+
12
+ from power_grid_model._core.buffer_handling import (
13
+ BufferProperties,
14
+ CAttributeBuffer,
15
+ CBuffer,
16
+ create_buffer,
17
+ get_buffer_properties,
18
+ get_buffer_view,
19
+ )
20
+ from power_grid_model._core.data_types import (
21
+ AttributeType,
22
+ ColumnarData,
23
+ ComponentData,
24
+ Dataset,
25
+ DenseBatchColumnarData,
26
+ SingleColumnarData,
27
+ SparseBatchColumnarData,
28
+ )
29
+ from power_grid_model._core.dataset_definitions import ComponentType, DatasetType, _str_to_component_type
30
+ from power_grid_model._core.enum import ComponentAttributeFilterOptions
31
+ from power_grid_model._core.error_handling import VALIDATOR_MSG, assert_no_error
32
+ from power_grid_model._core.power_grid_core import (
33
+ ConstDatasetPtr,
34
+ DatasetInfoPtr,
35
+ MutableDatasetPtr,
36
+ WritableDatasetPtr,
37
+ get_power_grid_core as get_pgc,
38
+ )
39
+ from power_grid_model._core.power_grid_meta import ComponentMetaData, DatasetMetaData, power_grid_meta_data
40
+ from power_grid_model._core.typing import ComponentAttributeMapping, ComponentAttributeMappingDict
41
+ from power_grid_model._core.utils import (
42
+ get_dataset_type,
43
+ is_columnar,
44
+ is_nan_or_equivalent,
45
+ is_sparse,
46
+ process_data_filter,
47
+ )
48
+
49
+
50
+ class CDatasetInfo:
51
+ """
52
+ Raw Power Grid Model dataset info.
53
+ """
54
+
55
+ def __init__(self, info: DatasetInfoPtr):
56
+ self._info: DatasetInfoPtr = info
57
+
58
+ def name(self) -> str:
59
+ """
60
+ The name of the dataset type.
61
+
62
+ Returns:
63
+ The name of the dataset type
64
+ """
65
+ return get_pgc().dataset_info_name(self._info)
66
+
67
+ def dataset_type(self):
68
+ """
69
+ The name of the dataset type.
70
+
71
+ Returns:
72
+ The name of the dataset type
73
+ """
74
+ return self.name()
75
+
76
+ def is_batch(self) -> bool:
77
+ """
78
+ Whether the dataset is a batch dataset.
79
+
80
+ Returns:
81
+ Whether the dataset is a batch dataset
82
+ """
83
+ return bool(get_pgc().dataset_info_is_batch(self._info))
84
+
85
+ def batch_size(self) -> int:
86
+ """
87
+ The size of the dataset.
88
+
89
+ Returns:
90
+ The size of the dataset
91
+ """
92
+ return get_pgc().dataset_info_batch_size(self._info)
93
+
94
+ def n_components(self) -> int:
95
+ """
96
+ The amount of components in the dataset.
97
+
98
+ Returns:
99
+ The amount of components in the dataset
100
+ """
101
+ return get_pgc().dataset_info_n_components(self._info)
102
+
103
+ def components(self) -> list[ComponentType]:
104
+ """
105
+ The components in the dataset.
106
+
107
+ Returns:
108
+ A list of the component names in the dataset
109
+ """
110
+ return [
111
+ _str_to_component_type(get_pgc().dataset_info_component_name(self._info, idx))
112
+ for idx in range(self.n_components())
113
+ ]
114
+
115
+ def elements_per_scenario(self) -> Mapping[ComponentType, int]:
116
+ """
117
+ The number of elements per scenario in the dataset.
118
+
119
+ Returns:
120
+ The number of elements per senario for each component in the dataset;
121
+ or -1 if the scenario is not uniform (different amount per scenario)
122
+ """
123
+ return {
124
+ component_name: get_pgc().dataset_info_elements_per_scenario(self._info, idx)
125
+ for idx, component_name in enumerate(self.components())
126
+ }
127
+
128
+ def total_elements(self) -> Mapping[ComponentType, int]:
129
+ """
130
+ The total number of elements in the dataset.
131
+
132
+ Returns:
133
+ The total number of elements for each component.
134
+ For each component, if the number of elements per scenario is uniform, its value shall be equal to
135
+ the product of the batch size and the amount of elements per scenario for that component.
136
+ """
137
+ return {
138
+ component_name: get_pgc().dataset_info_total_elements(self._info, idx)
139
+ for idx, component_name in enumerate(self.components())
140
+ }
141
+
142
+ def attribute_indications(self) -> Mapping[ComponentType, None | list[AttributeType]]:
143
+ """
144
+ The attribute indications in the dataset.
145
+
146
+ Returns:
147
+ A map of component to its attribute indications.
148
+ None means no attribute indications
149
+ """
150
+ result_dict: dict[ComponentType, None | list[AttributeType]] = {}
151
+ components = self.components()
152
+ for component_idx, component_name in enumerate(components):
153
+ has_indications = get_pgc().dataset_info_has_attribute_indications(self._info, component_idx)
154
+ if has_indications == 0:
155
+ result_dict[component_name] = None
156
+ else:
157
+ n_indications = get_pgc().dataset_info_n_attribute_indications(self._info, component_idx)
158
+ result_dict[component_name] = [
159
+ get_pgc().dataset_info_attribute_name(self._info, component_idx, attribute_idx)
160
+ for attribute_idx in range(n_indications)
161
+ ]
162
+ return result_dict
163
+
164
+
165
+ class CMutableDataset:
166
+ """
167
+ A view of a user-owned dataset.
168
+
169
+ This may be used to provide a user dataset to the Power Grid Model.
170
+
171
+ The dataset will create mutable buffers that the Power Grid Model can use to load data.
172
+ """
173
+
174
+ _dataset_type: DatasetType
175
+ _schema: DatasetMetaData
176
+ _is_batch: bool
177
+ _batch_size: int
178
+ _mutable_dataset: MutableDatasetPtr
179
+ _buffer_views: list[CBuffer]
180
+
181
+ def __new__(cls, data: Dataset, dataset_type: Any = None):
182
+ instance = super().__new__(cls)
183
+ instance._mutable_dataset = MutableDatasetPtr()
184
+ instance._buffer_views = []
185
+
186
+ instance._dataset_type = dataset_type if dataset_type in DatasetType else get_dataset_type(data)
187
+ instance._schema = power_grid_meta_data[instance._dataset_type]
188
+
189
+ if data:
190
+ first_component, first_component_data = next(iter(data.items()))
191
+ first_sub_info = get_buffer_properties(data=first_component_data, schema=instance._schema[first_component])
192
+ instance._is_batch = first_sub_info.is_batch
193
+ instance._batch_size = first_sub_info.batch_size
194
+ else:
195
+ instance._is_batch = False
196
+ instance._batch_size = 1
197
+
198
+ instance._mutable_dataset = get_pgc().create_dataset_mutable(
199
+ instance._dataset_type.value, instance._is_batch, instance._batch_size
200
+ )
201
+ assert_no_error()
202
+
203
+ instance._add_data(data)
204
+ assert_no_error()
205
+
206
+ return instance
207
+
208
+ def get_dataset_ptr(self) -> MutableDatasetPtr:
209
+ """
210
+ Get the raw underlying mutable dataset pointer.
211
+
212
+ Returns:
213
+ MutableDatasetPtr: the raw underlying mutable dataset pointer.
214
+ """
215
+ return self._mutable_dataset
216
+
217
+ def get_info(self) -> CDatasetInfo:
218
+ """
219
+ Get the info for this dataset.
220
+
221
+ Returns:
222
+ The dataset info for this dataset.
223
+ """
224
+ return CDatasetInfo(get_pgc().dataset_mutable_get_info(self._mutable_dataset))
225
+
226
+ def get_buffer_views(self) -> list[CBuffer]:
227
+ """
228
+ Get list of buffer views
229
+
230
+ Returns:
231
+ list of buffer view
232
+ """
233
+ return self._buffer_views
234
+
235
+ def _add_data(self, data: Dataset):
236
+ """
237
+ Add Power Grid Model data to the mutable dataset view.
238
+
239
+ Args:
240
+ data: the data.
241
+
242
+ Raises:
243
+ ValueError: if the component is unknown.
244
+ ValueError: if the data is inconsistent with the rest of the dataset.
245
+ PowerGridError: if there was an internal error.
246
+ """
247
+ for component, component_data in data.items():
248
+ self._add_component_data(component, component_data, allow_unknown=False)
249
+
250
+ def _add_component_data(self, component: ComponentType, data: ComponentData, allow_unknown: bool = False):
251
+ """
252
+ Add Power Grid Model data for a single component to the mutable dataset view.
253
+
254
+ Args:
255
+ component: the name of the component
256
+ data: the data of the component
257
+ allow_unknown (optional): ignore any unknown components. Defaults to False.
258
+
259
+ Raises:
260
+ ValueError: if the component is unknown and allow_unknown is False.
261
+ ValueError: if the data is inconsistent with the rest of the dataset.
262
+ PowerGridError: if there was an internal error.
263
+ """
264
+ if component not in self._schema:
265
+ if not allow_unknown:
266
+ raise ValueError(f"Unknown component {component} in schema. {VALIDATOR_MSG}")
267
+ return
268
+
269
+ self._validate_properties(data, self._schema[component])
270
+ c_buffer = get_buffer_view(data, self._schema[component], self._is_batch, self._batch_size)
271
+ self._buffer_views.append(c_buffer)
272
+ self._register_buffer(component, c_buffer)
273
+
274
+ def _register_buffer(self, component: ComponentType, buffer: CBuffer):
275
+ get_pgc().dataset_mutable_add_buffer(
276
+ dataset=self._mutable_dataset,
277
+ component=component.value,
278
+ elements_per_scenario=buffer.n_elements_per_scenario,
279
+ total_elements=buffer.total_elements,
280
+ indptr=buffer.indptr,
281
+ data=buffer.data,
282
+ )
283
+ assert_no_error()
284
+ for attr, attr_data in buffer.attribute_data.items():
285
+ self._register_attribute_buffer(component, attr, attr_data)
286
+
287
+ def _register_attribute_buffer(self, component, attr, attr_data):
288
+ get_pgc().dataset_mutable_add_attribute_buffer(
289
+ dataset=self._mutable_dataset,
290
+ component=component.value,
291
+ attribute=attr,
292
+ data=attr_data.data,
293
+ )
294
+ assert_no_error()
295
+
296
+ def _validate_properties(self, data: ComponentData, schema: ComponentMetaData):
297
+ properties = get_buffer_properties(data, schema=schema, is_batch=None, batch_size=None)
298
+ if properties.is_batch != self._is_batch:
299
+ raise ValueError(
300
+ f"Dataset type (single or batch) must be consistent across all components. {VALIDATOR_MSG}"
301
+ )
302
+ if properties.batch_size != self._batch_size:
303
+ raise ValueError(f"Dataset must have a consistent batch size across all components. {VALIDATOR_MSG}")
304
+
305
+ def __del__(self):
306
+ get_pgc().destroy_dataset_mutable(self._mutable_dataset)
307
+
308
+
309
+ class CConstDataset:
310
+ """
311
+ A view of a user-owned dataset.
312
+
313
+ This may be used to provide a user dataset to the Power Grid Model.
314
+
315
+ The dataset will create const buffers that the Power Grid Model can use to load data.
316
+
317
+ It is created from mutable dataset.
318
+ """
319
+
320
+ _const_dataset: ConstDatasetPtr
321
+ _buffer_views: list[CBuffer]
322
+
323
+ def __new__(cls, data: Dataset, dataset_type: DatasetType | None = None):
324
+ instance = super().__new__(cls)
325
+ instance._const_dataset = ConstDatasetPtr()
326
+
327
+ # create from mutable dataset
328
+ mutable_dataset = CMutableDataset(data=data, dataset_type=dataset_type)
329
+ instance._const_dataset = get_pgc().create_dataset_const_from_mutable(mutable_dataset.get_dataset_ptr())
330
+ assert_no_error()
331
+ instance._buffer_views = mutable_dataset.get_buffer_views()
332
+
333
+ return instance
334
+
335
+ def get_dataset_ptr(self) -> ConstDatasetPtr:
336
+ """
337
+ Get the raw underlying const dataset pointer.
338
+
339
+ Returns:
340
+ ConstDatasetPtr: the raw underlying const dataset pointer.
341
+ """
342
+ return self._const_dataset
343
+
344
+ def get_info(self) -> CDatasetInfo:
345
+ """
346
+ Get the info for this dataset.
347
+
348
+ Returns:
349
+ The dataset info for this dataset.
350
+ """
351
+ return CDatasetInfo(get_pgc().dataset_const_get_info(self._const_dataset))
352
+
353
+ def set_next_cartesian_product_dimension(self, next_dataset: "CConstDataset") -> None:
354
+ """
355
+ Set the next dataset in the linked list.
356
+
357
+ Args:
358
+ next_dataset: The next dataset to set.
359
+ """
360
+ get_pgc().dataset_const_set_next_cartesian_product_dimension(self._const_dataset, next_dataset._const_dataset)
361
+ assert_no_error()
362
+
363
+ def __del__(self):
364
+ get_pgc().destroy_dataset_const(self._const_dataset)
365
+
366
+
367
+ class CWritableDataset:
368
+ """
369
+ A view of a Power Grid Model-owned dataset.
370
+
371
+ This may be used to retrieve data from the Power Grid Model.
372
+
373
+ This class provides buffers to which the Power Grid Model can write data in an external call.
374
+ After writing to the buffers, the data contents can be retrieved.
375
+ """
376
+
377
+ def __init__(self, dataset_ptr: WritableDatasetPtr, data_filter: ComponentAttributeMapping):
378
+ self._writable_dataset = dataset_ptr
379
+
380
+ info = self.get_info()
381
+ self._dataset_type = info.dataset_type()
382
+ self._schema = power_grid_meta_data[self._dataset_type]
383
+
384
+ self._data_filter = process_data_filter(
385
+ dataset_type=info.dataset_type(),
386
+ data_filter=data_filter,
387
+ available_components=info.components(),
388
+ )
389
+
390
+ self._data: Dataset = {}
391
+ self._buffers: Mapping[str, CBuffer] = {}
392
+ self._component_buffer_properties = self._get_buffer_properties(info)
393
+
394
+ self._add_buffers()
395
+ assert_no_error()
396
+
397
+ def get_dataset_ptr(self) -> WritableDatasetPtr:
398
+ """
399
+ Get the raw underlying writable dataset pointer.
400
+
401
+ Returns:
402
+ WritableDatasetPtr: the raw underlying writable dataset pointer.
403
+ """
404
+ return self._writable_dataset
405
+
406
+ def get_info(self) -> CDatasetInfo:
407
+ """
408
+ Get the info for this dataset.
409
+
410
+ Returns:
411
+ The dataset info for this dataset.
412
+ """
413
+ return CDatasetInfo(get_pgc().dataset_writable_get_info(self._writable_dataset))
414
+
415
+ def get_data(self) -> Dataset:
416
+ """
417
+ Retrieve data from the Power Grid Model dataset.
418
+
419
+ The Power Grid Model may write to these buffers at a later point in time.
420
+
421
+ Returns:
422
+ The full dataset with filters applied.
423
+ """
424
+ self._post_filtering()
425
+ return self._data
426
+
427
+ def get_component_data(self, component: ComponentType) -> ComponentData:
428
+ """
429
+ Retrieve Power Grid Model data from the dataset for a specific component.
430
+
431
+ Args:
432
+ component: the component.
433
+
434
+ Returns:
435
+ The dataset for the specified component.
436
+ """
437
+ return self._data[component]
438
+
439
+ def get_data_filter(self) -> ComponentAttributeMappingDict:
440
+ """Gets the data filter requested
441
+
442
+ Returns:
443
+ ComponentAttributeMappingDict: data filter
444
+ """
445
+ return self._data_filter
446
+
447
+ def _add_buffers(self):
448
+ for component, buffer_properties in self._component_buffer_properties.items():
449
+ self._add_buffer(component, buffer_properties)
450
+
451
+ def _add_buffer(self, component: ComponentType, buffer_properties: BufferProperties):
452
+ schema = self._schema[component]
453
+
454
+ self._data[component] = create_buffer(buffer_properties, schema)
455
+ self._register_buffer(component, get_buffer_view(self._data[component], schema))
456
+
457
+ def _register_buffer(self, component: ComponentType, buffer: CBuffer):
458
+ get_pgc().dataset_writable_set_buffer(
459
+ dataset=self._writable_dataset,
460
+ component=component,
461
+ indptr=buffer.indptr,
462
+ data=buffer.data,
463
+ )
464
+ assert_no_error()
465
+ for attribute, attribute_data in buffer.attribute_data.items():
466
+ self._register_attribute_buffer(component, attribute, attribute_data)
467
+
468
+ def _register_attribute_buffer(
469
+ self,
470
+ component: ComponentType,
471
+ attribute: AttributeType,
472
+ buffer: CAttributeBuffer,
473
+ ):
474
+ get_pgc().dataset_writable_set_attribute_buffer(
475
+ dataset=self._writable_dataset,
476
+ component=component,
477
+ attribute=attribute,
478
+ data=buffer.data,
479
+ )
480
+ assert_no_error()
481
+
482
+ def _get_buffer_properties(self, info: CDatasetInfo) -> Mapping[ComponentType, BufferProperties]:
483
+ is_batch = info.is_batch()
484
+ batch_size = info.batch_size()
485
+ components = info.components()
486
+ n_elements_per_scenario = info.elements_per_scenario()
487
+ n_total_elements = info.total_elements()
488
+ attribute_indications = info.attribute_indications()
489
+
490
+ return {
491
+ component: BufferProperties(
492
+ is_sparse=n_elements_per_scenario[component] == -1,
493
+ is_batch=is_batch,
494
+ batch_size=batch_size,
495
+ n_elements_per_scenario=n_elements_per_scenario[component],
496
+ n_total_elements=n_total_elements[component],
497
+ columns=_get_filtered_attributes(
498
+ schema=self._schema[component],
499
+ component_data_filter=self._data_filter[component],
500
+ attribute_indication=attribute_indications[component],
501
+ ),
502
+ )
503
+ for component in components
504
+ if component in self._data_filter
505
+ }
506
+
507
+ def _filter_attributes(self, buffer: ColumnarData):
508
+ if is_sparse(buffer):
509
+ attributes = cast(SparseBatchColumnarData, buffer)["data"]
510
+ else:
511
+ attributes = cast(SingleColumnarData | DenseBatchColumnarData, buffer)
512
+
513
+ keys_to_remove = []
514
+ for attr, array in attributes.items():
515
+ if is_nan_or_equivalent(array):
516
+ keys_to_remove.append(attr)
517
+ for key in keys_to_remove:
518
+ del attributes[key]
519
+
520
+ def _filter_with_mapping(self):
521
+ for component_type, component_buffer in self._data.items():
522
+ if component_type in self._data_filter:
523
+ filter_option = self._data_filter[component_type]
524
+ if filter_option is ComponentAttributeFilterOptions.relevant and is_columnar(component_buffer):
525
+ self._filter_attributes(component_buffer)
526
+
527
+ def _post_filtering(self):
528
+ if isinstance(self._data_filter, dict):
529
+ self._filter_with_mapping()
530
+
531
+
532
+ def _get_filtered_attributes(
533
+ schema: ComponentMetaData,
534
+ component_data_filter: set[str] | list[str] | None | ComponentAttributeFilterOptions,
535
+ attribute_indication: None | list[AttributeType],
536
+ ) -> list[AttributeType] | None:
537
+ if component_data_filter is None:
538
+ return None
539
+
540
+ if isinstance(component_data_filter, ComponentAttributeFilterOptions):
541
+ if component_data_filter == ComponentAttributeFilterOptions.relevant and attribute_indication is not None:
542
+ return attribute_indication
543
+ return [] if schema.dtype.names is None else list(schema.dtype.names)
544
+
545
+ return list(component_data_filter)