power-grid-model 1.10.17__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 -198
  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 -562
  13. power_grid_model/_core/power_grid_dataset.py +545 -490
  14. power_grid_model/_core/power_grid_meta.py +262 -244
  15. power_grid_model/_core/power_grid_model.py +1025 -687
  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 -119
  52. power_grid_model/typing.py +43 -48
  53. power_grid_model/utils.py +529 -400
  54. power_grid_model/validation/__init__.py +25 -10
  55. power_grid_model/validation/{rules.py → _rules.py} +1167 -962
  56. power_grid_model/validation/{validation.py → _validation.py} +1172 -1015
  57. power_grid_model/validation/assertions.py +93 -92
  58. power_grid_model/validation/errors.py +602 -524
  59. power_grid_model/validation/utils.py +313 -318
  60. {power_grid_model-1.10.17.dist-info → power_grid_model-1.12.119.dist-info}/METADATA +162 -165
  61. power_grid_model-1.12.119.dist-info/RECORD +65 -0
  62. {power_grid_model-1.10.17.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.17.dist-info/RECORD +0 -32
  66. power_grid_model-1.10.17.dist-info/top_level.txt +0 -1
  67. {power_grid_model-1.10.17.dist-info → power_grid_model-1.12.119.dist-info/licenses}/LICENSE +0 -0
@@ -1,562 +1,577 @@
1
- # SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
2
- #
3
- # SPDX-License-Identifier: MPL-2.0
4
-
5
- """
6
- Loader for the dynamic library
7
- """
8
-
9
- import platform
10
- from ctypes import CDLL, POINTER, c_char, c_char_p, c_double, c_size_t, c_void_p
11
- from inspect import signature
12
- from itertools import chain
13
- from pathlib import Path
14
- from typing import Callable
15
-
16
- from power_grid_model._core.index_integer import IdC, IdxC
17
-
18
- # integer index
19
- IdxPtr = POINTER(IdxC)
20
- """Pointer to index."""
21
- IdxDoublePtr = POINTER(IdxPtr)
22
- """Double pointer to index."""
23
- IDPtr = POINTER(IdC)
24
- """Raw pointer to ids."""
25
-
26
- # string data
27
- CStr = c_char_p
28
- """Null terminated string."""
29
- CStrPtr = POINTER(CStr)
30
- """Pointer to null terminated string."""
31
- CharPtr = POINTER(c_char)
32
- """Raw pointer to char."""
33
- CharDoublePtr = POINTER(CharPtr)
34
- """Double pointer to char."""
35
-
36
- # raw data
37
- VoidPtr = c_void_p
38
- """Raw void pointer."""
39
- VoidDoublePtr = POINTER(c_void_p)
40
- """Double pointer to void."""
41
-
42
- # functions with size_t return
43
- _FUNC_SIZE_T_RES = {"meta_class_size", "meta_class_alignment", "meta_attribute_offset"}
44
- _ARGS_TYPE_MAPPING = {bytes: CharPtr, str: CStr, int: IdxC, float: c_double}
45
-
46
- # The c_void_p is extended only for type hinting and type checking; therefore no public methods are required.
47
- # pylint: disable=too-few-public-methods
48
-
49
-
50
- class HandlePtr(c_void_p):
51
- """
52
- Pointer to handle
53
- """
54
-
55
-
56
- class OptionsPtr(c_void_p):
57
- """
58
- Pointer to option
59
- """
60
-
61
-
62
- class ModelPtr(c_void_p):
63
- """
64
- Pointer to model
65
- """
66
-
67
-
68
- class DatasetPtr(c_void_p):
69
- """
70
- Pointer to dataset
71
- """
72
-
73
-
74
- class ConstDatasetPtr(c_void_p):
75
- """
76
- Pointer to writable dataset
77
- """
78
-
79
-
80
- class MutableDatasetPtr(c_void_p):
81
- """
82
- Pointer to writable dataset
83
- """
84
-
85
-
86
- class WritableDatasetPtr(c_void_p):
87
- """
88
- Pointer to writable dataset
89
- """
90
-
91
-
92
- class DatasetInfoPtr(c_void_p):
93
- """
94
- Pointer to dataset info
95
- """
96
-
97
-
98
- class ComponentPtr(c_void_p):
99
- """
100
- Pointer to component
101
- """
102
-
103
-
104
- class AttributePtr(c_void_p):
105
- """
106
- Pointer to attribute
107
- """
108
-
109
-
110
- class DeserializerPtr(c_void_p):
111
- """
112
- Pointer to deserializer
113
- """
114
-
115
-
116
- class SerializerPtr(c_void_p):
117
- """
118
- Pointer to serializer
119
- """
120
-
121
-
122
- def _load_core() -> CDLL:
123
- """
124
-
125
- Returns: DLL/SO object
126
-
127
- """
128
- if platform.system() == "Windows":
129
- dll_file = "_power_grid_core.dll"
130
- else:
131
- dll_file = "_power_grid_core.so"
132
- cdll = CDLL(str(Path(__file__).parent / dll_file))
133
- # assign return types
134
- # handle
135
- cdll.PGM_create_handle.argtypes = []
136
- cdll.PGM_create_handle.restype = HandlePtr
137
- cdll.PGM_destroy_handle.argtypes = [HandlePtr]
138
- cdll.PGM_destroy_handle.restype = None
139
- return cdll
140
-
141
-
142
- # load dll once
143
- _CDLL: CDLL = _load_core()
144
-
145
-
146
- def make_c_binding(func: Callable):
147
- """
148
- Descriptor to make the function to bind to C
149
-
150
- Args:
151
- func: method object from PowerGridCore
152
-
153
- Returns:
154
- Binded function
155
-
156
- """
157
- name = func.__name__
158
- sig = signature(func)
159
-
160
- # get and convert types, skip first argument, as it is self
161
- py_argnames = list(sig.parameters.keys())[1:]
162
- py_argtypes = [v.annotation for v in sig.parameters.values()][1:]
163
- py_restype = sig.return_annotation
164
- c_argtypes = [_ARGS_TYPE_MAPPING.get(x, x) for x in py_argtypes]
165
- c_restype = _ARGS_TYPE_MAPPING.get(py_restype, py_restype)
166
- if c_restype == IdxC and name in _FUNC_SIZE_T_RES:
167
- c_restype = c_size_t
168
- # set argument in dll
169
- # mostly with handle pointer, except destroy function
170
- is_destroy_func = "destroy" in name
171
- if is_destroy_func:
172
- getattr(_CDLL, f"PGM_{name}").argtypes = c_argtypes
173
- else:
174
- getattr(_CDLL, f"PGM_{name}").argtypes = [HandlePtr] + c_argtypes
175
- getattr(_CDLL, f"PGM_{name}").restype = c_restype
176
-
177
- # binding function
178
- def cbind_func(self, *args, **kwargs):
179
- if "destroy" in name:
180
- c_inputs = []
181
- else:
182
- c_inputs = [self._handle] # pylint: disable=protected-access
183
- args = chain(args, (kwargs[key] for key in py_argnames[len(args) :]))
184
- for arg in args:
185
- if isinstance(arg, str):
186
- c_inputs.append(arg.encode())
187
- else:
188
- c_inputs.append(arg)
189
-
190
- # call
191
- res = getattr(_CDLL, f"PGM_{name}")(*c_inputs)
192
- # convert to string for CStr
193
- if c_restype == CStr:
194
- res = res.decode() if res is not None else ""
195
- return res
196
-
197
- return cbind_func
198
-
199
-
200
- # pylint: disable=too-many-arguments
201
- # pylint: disable=missing-function-docstring
202
- # pylint: disable=too-many-public-methods
203
- class PowerGridCore:
204
- """
205
- DLL caller
206
- """
207
-
208
- _handle: HandlePtr
209
- _instance: "PowerGridCore | None" = None
210
-
211
- # singleton of power grid core
212
- def __new__(cls, *args, **kwargs):
213
- if cls._instance is None:
214
- cls._instance = super().__new__(cls, *args, **kwargs)
215
- cls._instance._handle = _CDLL.PGM_create_handle()
216
- return cls._instance
217
-
218
- def __del__(self):
219
- _CDLL.PGM_destroy_handle(self._handle)
220
-
221
- # not copyable
222
- def __copy__(self):
223
- raise NotImplementedError("Class not copyable")
224
-
225
- def __deepcopy__(self, memodict):
226
- raise NotImplementedError("class not copyable")
227
-
228
- @make_c_binding
229
- def error_code(self) -> int: # type: ignore[empty-body]
230
- pass # pragma: no cover
231
-
232
- @make_c_binding
233
- def error_message(self) -> str: # type: ignore[empty-body]
234
- pass # pragma: no cover
235
-
236
- @make_c_binding
237
- def n_failed_scenarios(self) -> int: # type: ignore[empty-body]
238
- pass # pragma: no cover
239
-
240
- @make_c_binding
241
- def failed_scenarios(self) -> IdxPtr: # type: ignore[empty-body, valid-type] # type: ignore[empty-body]
242
- pass # pragma: no cover
243
-
244
- @make_c_binding
245
- def batch_errors(self) -> CStrPtr: # type: ignore[empty-body, valid-type] # type: ignore[empty-body]
246
- pass # pragma: no cover
247
-
248
- @make_c_binding
249
- def clear_error(self) -> None: # type: ignore[empty-body]
250
- pass # pragma: no cover
251
-
252
- @make_c_binding
253
- def meta_n_datasets(self) -> int: # type: ignore[empty-body]
254
- pass # pragma: no cover
255
-
256
- @make_c_binding
257
- def meta_get_dataset_by_idx(self, idx: int) -> DatasetPtr: # type: ignore[empty-body]
258
- pass # pragma: no cover
259
-
260
- @make_c_binding
261
- def meta_dataset_name(self, dataset: DatasetPtr) -> str: # type: ignore[empty-body]
262
- pass # pragma: no cover
263
-
264
- @make_c_binding
265
- def meta_n_components(self, dataset: DatasetPtr) -> int: # type: ignore[empty-body]
266
- pass # pragma: no cover
267
-
268
- @make_c_binding
269
- def meta_get_component_by_idx(self, dataset: DatasetPtr, idx: int) -> ComponentPtr: # type: ignore[empty-body]
270
- pass # pragma: no cover
271
-
272
- @make_c_binding
273
- def meta_component_name(self, component: ComponentPtr) -> str: # type: ignore[empty-body]
274
- pass # pragma: no cover
275
-
276
- @make_c_binding
277
- def meta_component_alignment(self, component: ComponentPtr) -> int: # type: ignore[empty-body]
278
- pass # pragma: no cover
279
-
280
- @make_c_binding
281
- def meta_component_size(self, component: ComponentPtr) -> int: # type: ignore[empty-body]
282
- pass # pragma: no cover
283
-
284
- @make_c_binding
285
- def meta_n_attributes(self, component: ComponentPtr) -> int: # type: ignore[empty-body]
286
- pass # pragma: no cover
287
-
288
- @make_c_binding
289
- def meta_get_attribute_by_idx(self, component: ComponentPtr, idx: int) -> AttributePtr: # type: ignore[empty-body]
290
- pass # pragma: no cover
291
-
292
- @make_c_binding
293
- def meta_attribute_name(self, attribute: AttributePtr) -> str: # type: ignore[empty-body]
294
- pass # pragma: no cover
295
-
296
- @make_c_binding
297
- def meta_attribute_ctype(self, attribute: AttributePtr) -> int: # type: ignore[empty-body]
298
- pass # pragma: no cover
299
-
300
- @make_c_binding
301
- def meta_attribute_offset(self, attribute: AttributePtr) -> int: # type: ignore[empty-body]
302
- pass # pragma: no cover
303
-
304
- @make_c_binding
305
- def is_little_endian(self) -> int: # type: ignore[empty-body]
306
- pass # pragma: no cover
307
-
308
- @make_c_binding
309
- def create_options(self) -> OptionsPtr: # type: ignore[empty-body]
310
- pass # pragma: no cover
311
-
312
- @make_c_binding
313
- def destroy_options(self, opt: OptionsPtr) -> None: # type: ignore[empty-body]
314
- pass # pragma: no cover
315
-
316
- @make_c_binding
317
- def set_calculation_type(self, opt: OptionsPtr, calculation_type: int) -> None: # type: ignore[empty-body]
318
- pass # pragma: no cover
319
-
320
- @make_c_binding
321
- def set_calculation_method(self, opt: OptionsPtr, method: int) -> None: # type: ignore[empty-body]
322
- pass # pragma: no cover
323
-
324
- @make_c_binding
325
- def set_tap_changing_strategy(
326
- self, opt: OptionsPtr, tap_changing_strategy: int
327
- ) -> None: # type: ignore[empty-body]
328
- pass # pragma: no cover
329
-
330
- @make_c_binding
331
- def set_short_circuit_voltage_scaling(
332
- self, opt: OptionsPtr, short_circuit_voltage_scaling: int
333
- ) -> None: # type: ignore[empty-body]
334
- pass # pragma: no cover
335
-
336
- @make_c_binding
337
- def set_experimental_features(
338
- self, opt: OptionsPtr, experimental_features: int
339
- ) -> None: # type: ignore[empty-body]
340
- pass # pragma: no cover
341
-
342
- @make_c_binding
343
- def set_symmetric(self, opt: OptionsPtr, sym: int) -> None: # type: ignore[empty-body]
344
- pass # pragma: no cover
345
-
346
- @make_c_binding
347
- def set_err_tol(self, opt: OptionsPtr, err_tol: float) -> None: # type: ignore[empty-body]
348
- pass # pragma: no cover
349
-
350
- @make_c_binding
351
- def set_max_iter(self, opt: OptionsPtr, max_iter: int) -> None: # type: ignore[empty-body]
352
- pass # pragma: no cover
353
-
354
- @make_c_binding
355
- def set_threading(self, opt: OptionsPtr, threading: int) -> None: # type: ignore[empty-body]
356
- pass # pragma: no cover
357
-
358
- @make_c_binding
359
- def create_model( # type: ignore[empty-body]
360
- self,
361
- system_frequency: float,
362
- input_data: ConstDatasetPtr, # type: ignore[valid-type]
363
- ) -> ModelPtr:
364
- pass # pragma: no cover
365
-
366
- @make_c_binding
367
- def update_model( # type: ignore[empty-body]
368
- self,
369
- model: ModelPtr,
370
- update_data: ConstDatasetPtr, # type: ignore[valid-type]
371
- ) -> None:
372
- pass # pragma: no cover
373
-
374
- @make_c_binding
375
- def copy_model(self, model: ModelPtr) -> ModelPtr: # type: ignore[empty-body]
376
- pass # pragma: no cover
377
-
378
- @make_c_binding
379
- def get_indexer( # pylint: disable=too-many-positional-arguments
380
- self,
381
- model: ModelPtr,
382
- component: str,
383
- size: int,
384
- ids: IDPtr, # type: ignore[valid-type]
385
- indexer: IdxPtr, # type: ignore[valid-type]
386
- ) -> None: # type: ignore[empty-body]
387
- pass # pragma: no cover
388
-
389
- @make_c_binding
390
- def destroy_model(self, model: ModelPtr) -> None: # type: ignore[empty-body]
391
- pass # pragma: no cover
392
-
393
- @make_c_binding
394
- def calculate( # type: ignore[empty-body]
395
- self,
396
- model: ModelPtr,
397
- opt: OptionsPtr,
398
- output_data: MutableDatasetPtr, # type: ignore[valid-type]
399
- update_data: ConstDatasetPtr, # type: ignore[valid-type]
400
- ) -> None:
401
- pass # pragma: no cover
402
-
403
- @make_c_binding
404
- def dataset_info_name(self, info: DatasetInfoPtr) -> str: # type: ignore[empty-body]
405
- pass # pragma: no cover
406
-
407
- @make_c_binding
408
- def dataset_info_is_batch(self, info: DatasetInfoPtr) -> int: # type: ignore[empty-body]
409
- pass # pragma: no cover
410
-
411
- @make_c_binding
412
- def dataset_info_batch_size(self, info: DatasetInfoPtr) -> int: # type: ignore[empty-body]
413
- pass # pragma: no cover
414
-
415
- @make_c_binding
416
- def dataset_info_n_components(self, info: DatasetInfoPtr) -> int: # type: ignore[empty-body]
417
- pass # pragma: no cover
418
-
419
- @make_c_binding
420
- def dataset_info_component_name(self, info: DatasetInfoPtr, component_idx: int) -> str: # type: ignore[empty-body]
421
- pass # pragma: no cover
422
-
423
- @make_c_binding
424
- def dataset_info_elements_per_scenario( # type: ignore[empty-body]
425
- self, info: DatasetInfoPtr, component_idx: int
426
- ) -> int:
427
- pass # pragma: no cover
428
-
429
- @make_c_binding
430
- def dataset_info_total_elements(self, info: DatasetInfoPtr, component_idx: int) -> int: # type: ignore[empty-body]
431
- pass # pragma: no cover
432
-
433
- @make_c_binding
434
- def create_dataset_mutable( # type: ignore[empty-body]
435
- self, dataset: str, is_batch: int, batch_size: int
436
- ) -> MutableDatasetPtr:
437
- pass # pragma: no cover
438
-
439
- @make_c_binding
440
- def destroy_dataset_mutable(self, dataset: MutableDatasetPtr) -> None: # type: ignore[empty-body]
441
- pass # pragma: no cover
442
-
443
- @make_c_binding
444
- def dataset_mutable_add_buffer( # type: ignore[empty-body] # pylint: disable=too-many-positional-arguments
445
- self,
446
- dataset: MutableDatasetPtr,
447
- component: str,
448
- elements_per_scenario: int,
449
- total_elements: int,
450
- indptr: IdxPtr, # type: ignore[valid-type]
451
- data: VoidPtr, # type: ignore[valid-type]
452
- ) -> None:
453
- pass # pragma: no cover
454
-
455
- @make_c_binding
456
- def dataset_mutable_add_attribute_buffer(
457
- self,
458
- dataset: MutableDatasetPtr,
459
- component: str,
460
- attribute: str,
461
- data: VoidPtr, # type: ignore[valid-type]
462
- ) -> None: # type: ignore[empty-body]
463
- pass # pragma: no cover
464
-
465
- @make_c_binding
466
- def dataset_mutable_get_info(self, dataset: MutableDatasetPtr) -> DatasetInfoPtr: # type: ignore[empty-body]
467
- pass # pragma: no cover
468
-
469
- @make_c_binding
470
- def create_dataset_const_from_mutable( # type: ignore[empty-body]
471
- self, mutable_dataset: MutableDatasetPtr
472
- ) -> ConstDatasetPtr:
473
- pass # pragma: no cover
474
-
475
- @make_c_binding
476
- def destroy_dataset_const(self, dataset: ConstDatasetPtr) -> None: # type: ignore[empty-body]
477
- pass # pragma: no cover
478
-
479
- @make_c_binding
480
- def dataset_const_get_info(self, dataset: ConstDatasetPtr) -> DatasetInfoPtr: # type: ignore[empty-body]
481
- pass # pragma: no cover
482
-
483
- @make_c_binding
484
- def dataset_writable_get_info(self, dataset: WritableDatasetPtr) -> DatasetInfoPtr: # type: ignore[empty-body]
485
- pass # pragma: no cover
486
-
487
- @make_c_binding
488
- def dataset_writable_set_buffer(
489
- self,
490
- dataset: WritableDatasetPtr,
491
- component: str,
492
- indptr: IdxPtr, # type: ignore[valid-type]
493
- data: VoidPtr, # type: ignore[valid-type]
494
- ) -> None: # type: ignore[empty-body]
495
- pass # pragma: no cover
496
-
497
- @make_c_binding
498
- def dataset_writable_set_attribute_buffer(
499
- self,
500
- dataset: WritableDatasetPtr,
501
- component: str,
502
- attribute: str,
503
- data: VoidPtr, # type: ignore[valid-type]
504
- ) -> None: # type: ignore[empty-body]
505
- pass # pragma: no cover
506
-
507
- @make_c_binding
508
- def create_deserializer_from_binary_buffer( # type: ignore[empty-body]
509
- self, data: bytes, size: int, serialization_format: int
510
- ) -> DeserializerPtr:
511
- pass # pragma: no cover
512
-
513
- @make_c_binding
514
- def create_deserializer_from_null_terminated_string( # type: ignore[empty-body]
515
- self, data: str, serialization_format: int
516
- ) -> DeserializerPtr:
517
- pass # pragma: no cover
518
-
519
- @make_c_binding
520
- def deserializer_get_dataset(self, deserializer: DeserializerPtr) -> WritableDatasetPtr: # type: ignore[empty-body]
521
- pass # pragma: no cover
522
-
523
- @make_c_binding
524
- def deserializer_parse_to_buffer(self, deserializer: DeserializerPtr) -> None: # type: ignore[empty-body]
525
- pass # pragma: no cover
526
-
527
- @make_c_binding
528
- def destroy_deserializer(self, deserializer: DeserializerPtr) -> None: # type: ignore[empty-body]
529
- pass # pragma: no cover
530
-
531
- @make_c_binding
532
- def create_serializer( # type: ignore[empty-body]
533
- self, data: ConstDatasetPtr, serialization_format: int
534
- ) -> SerializerPtr:
535
- pass # pragma: no cover
536
-
537
- @make_c_binding
538
- def serializer_get_to_binary_buffer( # type: ignore[empty-body]
539
- self,
540
- serializer: SerializerPtr,
541
- use_compact_list: int,
542
- data: CharDoublePtr, # type: ignore[valid-type]
543
- size: IdxPtr, # type: ignore[valid-type]
544
- ) -> None:
545
- pass # pragma: no cover
546
-
547
- @make_c_binding
548
- def serializer_get_to_zero_terminated_string( # type: ignore[empty-body]
549
- self,
550
- serializer: SerializerPtr,
551
- use_compact_list: int,
552
- indent: int,
553
- ) -> str:
554
- pass # pragma: no cover
555
-
556
- @make_c_binding
557
- def destroy_serializer(self, serializer: SerializerPtr) -> None: # type: ignore[empty-body]
558
- pass # pragma: no cover
559
-
560
-
561
- # make one instance
562
- power_grid_core = PowerGridCore()
1
+ # SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
2
+ #
3
+ # SPDX-License-Identifier: MPL-2.0
4
+
5
+ """
6
+ Loader for the dynamic library
7
+ """
8
+
9
+ import threading
10
+ from collections.abc import Callable
11
+ from ctypes import CDLL, POINTER, c_char, c_char_p, c_double, c_size_t, c_void_p
12
+ from inspect import signature
13
+ from itertools import chain
14
+
15
+ from power_grid_model._core.index_integer import IdC, IdxC
16
+ from power_grid_model._core.power_grid_model_c.get_pgm_dll_path import get_pgm_dll_path
17
+
18
+ # threading local
19
+ _thread_local_data = threading.local()
20
+
21
+
22
+ # integer index
23
+ IdxPtr = POINTER(IdxC)
24
+ """Pointer to index."""
25
+ IdxDoublePtr = POINTER(IdxPtr)
26
+ """Double pointer to index."""
27
+ IDPtr = POINTER(IdC)
28
+ """Raw pointer to ids."""
29
+
30
+ # string data
31
+ CStr = c_char_p
32
+ """Null terminated string."""
33
+ CStrPtr = POINTER(CStr)
34
+ """Pointer to null terminated string."""
35
+ CharPtr = POINTER(c_char)
36
+ """Raw pointer to char."""
37
+ CharDoublePtr = POINTER(CharPtr)
38
+ """Double pointer to char."""
39
+
40
+ # raw data
41
+ VoidPtr = c_void_p
42
+ """Raw void pointer."""
43
+ VoidDoublePtr = POINTER(c_void_p)
44
+ """Double pointer to void."""
45
+
46
+ # functions with size_t return
47
+ _FUNC_SIZE_T_RES = {"meta_class_size", "meta_class_alignment", "meta_attribute_offset"}
48
+ _ARGS_TYPE_MAPPING = {bytes: CharPtr, str: CStr, int: IdxC, float: c_double}
49
+
50
+ # The c_void_p is extended only for type hinting and type checking; therefore no public methods are required.
51
+
52
+
53
+ class HandlePtr(c_void_p):
54
+ """
55
+ Pointer to handle
56
+ """
57
+
58
+
59
+ class OptionsPtr(c_void_p):
60
+ """
61
+ Pointer to option
62
+ """
63
+
64
+
65
+ class ModelPtr(c_void_p):
66
+ """
67
+ Pointer to model
68
+ """
69
+
70
+
71
+ class DatasetPtr(c_void_p):
72
+ """
73
+ Pointer to dataset
74
+ """
75
+
76
+
77
+ class ConstDatasetPtr(c_void_p):
78
+ """
79
+ Pointer to writable dataset
80
+ """
81
+
82
+
83
+ class MutableDatasetPtr(c_void_p):
84
+ """
85
+ Pointer to writable dataset
86
+ """
87
+
88
+
89
+ class WritableDatasetPtr(c_void_p):
90
+ """
91
+ Pointer to writable dataset
92
+ """
93
+
94
+
95
+ class DatasetInfoPtr(c_void_p):
96
+ """
97
+ Pointer to dataset info
98
+ """
99
+
100
+
101
+ class ComponentPtr(c_void_p):
102
+ """
103
+ Pointer to component
104
+ """
105
+
106
+
107
+ class AttributePtr(c_void_p):
108
+ """
109
+ Pointer to attribute
110
+ """
111
+
112
+
113
+ class DeserializerPtr(c_void_p):
114
+ """
115
+ Pointer to deserializer
116
+ """
117
+
118
+
119
+ class SerializerPtr(c_void_p):
120
+ """
121
+ Pointer to serializer
122
+ """
123
+
124
+
125
+ def _load_core() -> CDLL:
126
+ """
127
+
128
+ Returns: DLL/SO object
129
+
130
+ """
131
+ dll_path = get_pgm_dll_path()
132
+ cdll = CDLL(str(dll_path))
133
+ # assign return types
134
+ # handle
135
+ cdll.PGM_create_handle.argtypes = []
136
+ cdll.PGM_create_handle.restype = HandlePtr
137
+ cdll.PGM_destroy_handle.argtypes = [HandlePtr]
138
+ cdll.PGM_destroy_handle.restype = None
139
+ return cdll
140
+
141
+
142
+ # load dll once
143
+ _CDLL: CDLL = _load_core()
144
+
145
+
146
+ def make_c_binding(func: Callable):
147
+ """
148
+ Descriptor to make the function to bind to C
149
+
150
+ Args:
151
+ func: method object from PowerGridCore
152
+
153
+ Returns:
154
+ Binded function
155
+
156
+ """
157
+ name = func.__name__
158
+ sig = signature(func)
159
+
160
+ # get and convert types, skip first argument, as it is self
161
+ py_argnames = list(sig.parameters.keys())[1:]
162
+ py_argtypes = [v.annotation for v in sig.parameters.values()][1:]
163
+ py_restype = sig.return_annotation
164
+ c_argtypes = [_ARGS_TYPE_MAPPING.get(x, x) for x in py_argtypes]
165
+ c_restype = _ARGS_TYPE_MAPPING.get(py_restype, py_restype)
166
+ if c_restype == IdxC and name in _FUNC_SIZE_T_RES:
167
+ c_restype = c_size_t
168
+ # set argument in dll
169
+ # mostly with handle pointer, except destroy function
170
+ is_destroy_func = "destroy" in name
171
+ if is_destroy_func:
172
+ getattr(_CDLL, f"PGM_{name}").argtypes = c_argtypes
173
+ else:
174
+ getattr(_CDLL, f"PGM_{name}").argtypes = [HandlePtr, *c_argtypes]
175
+ getattr(_CDLL, f"PGM_{name}").restype = c_restype
176
+
177
+ # binding function
178
+ def cbind_func(self, *args, **kwargs):
179
+ c_inputs = [] if "destroy" in name else [self._handle]
180
+ args = chain(args, (kwargs[key] for key in py_argnames[len(args) :]))
181
+ for arg in args:
182
+ if isinstance(arg, str):
183
+ c_inputs.append(arg.encode())
184
+ else:
185
+ c_inputs.append(arg)
186
+
187
+ # call
188
+ res = getattr(_CDLL, f"PGM_{name}")(*c_inputs)
189
+ # convert to string for CStr
190
+ if c_restype == CStr:
191
+ res = res.decode() if res is not None else ""
192
+ return res
193
+
194
+ return cbind_func
195
+
196
+
197
+ class PowerGridCore:
198
+ """
199
+ DLL caller
200
+ """
201
+
202
+ _handle: HandlePtr
203
+
204
+ # singleton of power grid core
205
+ def __new__(cls, *args, **kwargs):
206
+ instance = super().__new__(cls, *args, **kwargs)
207
+ instance._handle = _CDLL.PGM_create_handle()
208
+ return instance
209
+
210
+ def __del__(self):
211
+ _CDLL.PGM_destroy_handle(self._handle)
212
+
213
+ # not copyable
214
+ def __copy__(self):
215
+ raise NotImplementedError("Class not copyable")
216
+
217
+ def __deepcopy__(self, memodict):
218
+ raise NotImplementedError("class not copyable")
219
+
220
+ @make_c_binding
221
+ def error_code(self) -> int: # type: ignore[empty-body]
222
+ pass # pragma: no cover
223
+
224
+ @make_c_binding
225
+ def error_message(self) -> str: # type: ignore[empty-body]
226
+ pass # pragma: no cover
227
+
228
+ @make_c_binding
229
+ def n_failed_scenarios(self) -> int: # type: ignore[empty-body]
230
+ pass # pragma: no cover
231
+
232
+ @make_c_binding
233
+ def failed_scenarios(self) -> IdxPtr: # type: ignore[empty-body, valid-type] # type: ignore[empty-body]
234
+ pass # pragma: no cover
235
+
236
+ @make_c_binding
237
+ def batch_errors(self) -> CStrPtr: # type: ignore[empty-body, valid-type] # type: ignore[empty-body]
238
+ pass # pragma: no cover
239
+
240
+ @make_c_binding
241
+ def clear_error(self) -> None: # type: ignore[empty-body]
242
+ pass # pragma: no cover
243
+
244
+ @make_c_binding
245
+ def meta_n_datasets(self) -> int: # type: ignore[empty-body]
246
+ pass # pragma: no cover
247
+
248
+ @make_c_binding
249
+ def meta_get_dataset_by_idx(self, idx: int) -> DatasetPtr: # type: ignore[empty-body]
250
+ pass # pragma: no cover
251
+
252
+ @make_c_binding
253
+ def meta_dataset_name(self, dataset: DatasetPtr) -> str: # type: ignore[empty-body]
254
+ pass # pragma: no cover
255
+
256
+ @make_c_binding
257
+ def meta_n_components(self, dataset: DatasetPtr) -> int: # type: ignore[empty-body]
258
+ pass # pragma: no cover
259
+
260
+ @make_c_binding
261
+ def meta_get_component_by_idx(self, dataset: DatasetPtr, idx: int) -> ComponentPtr: # type: ignore[empty-body]
262
+ pass # pragma: no cover
263
+
264
+ @make_c_binding
265
+ def meta_component_name(self, component: ComponentPtr) -> str: # type: ignore[empty-body]
266
+ pass # pragma: no cover
267
+
268
+ @make_c_binding
269
+ def meta_component_alignment(self, component: ComponentPtr) -> int: # type: ignore[empty-body]
270
+ pass # pragma: no cover
271
+
272
+ @make_c_binding
273
+ def meta_component_size(self, component: ComponentPtr) -> int: # type: ignore[empty-body]
274
+ pass # pragma: no cover
275
+
276
+ @make_c_binding
277
+ def meta_n_attributes(self, component: ComponentPtr) -> int: # type: ignore[empty-body]
278
+ pass # pragma: no cover
279
+
280
+ @make_c_binding
281
+ def meta_get_attribute_by_idx(self, component: ComponentPtr, idx: int) -> AttributePtr: # type: ignore[empty-body]
282
+ pass # pragma: no cover
283
+
284
+ @make_c_binding
285
+ def meta_attribute_name(self, attribute: AttributePtr) -> str: # type: ignore[empty-body]
286
+ pass # pragma: no cover
287
+
288
+ @make_c_binding
289
+ def meta_attribute_ctype(self, attribute: AttributePtr) -> int: # type: ignore[empty-body]
290
+ pass # pragma: no cover
291
+
292
+ @make_c_binding
293
+ def meta_attribute_offset(self, attribute: AttributePtr) -> int: # type: ignore[empty-body]
294
+ pass # pragma: no cover
295
+
296
+ @make_c_binding
297
+ def is_little_endian(self) -> int: # type: ignore[empty-body]
298
+ pass # pragma: no cover
299
+
300
+ @make_c_binding
301
+ def create_options(self) -> OptionsPtr: # type: ignore[empty-body]
302
+ pass # pragma: no cover
303
+
304
+ @make_c_binding
305
+ def destroy_options(self, opt: OptionsPtr) -> None: # type: ignore[empty-body]
306
+ pass # pragma: no cover
307
+
308
+ @make_c_binding
309
+ def set_calculation_type(self, opt: OptionsPtr, calculation_type: int) -> None: # type: ignore[empty-body]
310
+ pass # pragma: no cover
311
+
312
+ @make_c_binding
313
+ def set_calculation_method(self, opt: OptionsPtr, method: int) -> None: # type: ignore[empty-body]
314
+ pass # pragma: no cover
315
+
316
+ @make_c_binding
317
+ def set_tap_changing_strategy(self, opt: OptionsPtr, tap_changing_strategy: int) -> None: # type: ignore[empty-body]
318
+ pass # pragma: no cover
319
+
320
+ @make_c_binding
321
+ def set_short_circuit_voltage_scaling(self, opt: OptionsPtr, short_circuit_voltage_scaling: int) -> None: # type: ignore[empty-body]
322
+ pass # pragma: no cover
323
+
324
+ @make_c_binding
325
+ def set_experimental_features(self, opt: OptionsPtr, experimental_features: int) -> None: # type: ignore[empty-body]
326
+ pass # pragma: no cover
327
+
328
+ @make_c_binding
329
+ def set_symmetric(self, opt: OptionsPtr, sym: int) -> None: # type: ignore[empty-body]
330
+ pass # pragma: no cover
331
+
332
+ @make_c_binding
333
+ def set_err_tol(self, opt: OptionsPtr, err_tol: float) -> None: # type: ignore[empty-body]
334
+ pass # pragma: no cover
335
+
336
+ @make_c_binding
337
+ def set_max_iter(self, opt: OptionsPtr, max_iter: int) -> None: # type: ignore[empty-body]
338
+ pass # pragma: no cover
339
+
340
+ @make_c_binding
341
+ def set_threading(self, opt: OptionsPtr, threading: int) -> None: # type: ignore[empty-body]
342
+ pass # pragma: no cover
343
+
344
+ @make_c_binding
345
+ def create_model( # type: ignore[empty-body]
346
+ self,
347
+ system_frequency: float,
348
+ input_data: ConstDatasetPtr, # type: ignore[valid-type]
349
+ ) -> ModelPtr:
350
+ pass # pragma: no cover
351
+
352
+ @make_c_binding
353
+ def update_model( # type: ignore[empty-body]
354
+ self,
355
+ model: ModelPtr,
356
+ update_data: ConstDatasetPtr, # type: ignore[valid-type]
357
+ ) -> None:
358
+ pass # pragma: no cover
359
+
360
+ @make_c_binding
361
+ def copy_model(self, model: ModelPtr) -> ModelPtr: # type: ignore[empty-body]
362
+ pass # pragma: no cover
363
+
364
+ @make_c_binding
365
+ def get_indexer(
366
+ self,
367
+ model: ModelPtr,
368
+ component: str,
369
+ size: int,
370
+ ids: IDPtr, # type: ignore[valid-type]
371
+ indexer: IdxPtr, # type: ignore[valid-type]
372
+ ) -> None: # type: ignore[empty-body]
373
+ pass # pragma: no cover
374
+
375
+ @make_c_binding
376
+ def destroy_model(self, model: ModelPtr) -> None: # type: ignore[empty-body]
377
+ pass # pragma: no cover
378
+
379
+ @make_c_binding
380
+ def calculate( # type: ignore[empty-body]
381
+ self,
382
+ model: ModelPtr,
383
+ opt: OptionsPtr,
384
+ output_data: MutableDatasetPtr, # type: ignore[valid-type]
385
+ update_data: ConstDatasetPtr, # type: ignore[valid-type]
386
+ ) -> None:
387
+ pass # pragma: no cover
388
+
389
+ @make_c_binding
390
+ def dataset_info_name(self, info: DatasetInfoPtr) -> str: # type: ignore[empty-body]
391
+ pass # pragma: no cover
392
+
393
+ @make_c_binding
394
+ def dataset_info_is_batch(self, info: DatasetInfoPtr) -> int: # type: ignore[empty-body]
395
+ pass # pragma: no cover
396
+
397
+ @make_c_binding
398
+ def dataset_info_batch_size(self, info: DatasetInfoPtr) -> int: # type: ignore[empty-body]
399
+ pass # pragma: no cover
400
+
401
+ @make_c_binding
402
+ def dataset_info_n_components(self, info: DatasetInfoPtr) -> int: # type: ignore[empty-body]
403
+ pass # pragma: no cover
404
+
405
+ @make_c_binding
406
+ def dataset_info_component_name(self, info: DatasetInfoPtr, component_idx: int) -> str: # type: ignore[empty-body]
407
+ pass # pragma: no cover
408
+
409
+ @make_c_binding
410
+ def dataset_info_elements_per_scenario( # type: ignore[empty-body]
411
+ self, info: DatasetInfoPtr, component_idx: int
412
+ ) -> int:
413
+ pass # pragma: no cover
414
+
415
+ @make_c_binding
416
+ def dataset_info_total_elements(self, info: DatasetInfoPtr, component_idx: int) -> int: # type: ignore[empty-body]
417
+ pass # pragma: no cover
418
+
419
+ @make_c_binding
420
+ def dataset_info_has_attribute_indications( # type: ignore[empty-body]
421
+ self, info: DatasetInfoPtr, component_idx: int
422
+ ) -> int:
423
+ pass # pragma: no cover
424
+
425
+ @make_c_binding
426
+ def dataset_info_n_attribute_indications( # type: ignore[empty-body]
427
+ self, info: DatasetInfoPtr, component_idx: int
428
+ ) -> int:
429
+ pass # pragma: no cover
430
+
431
+ @make_c_binding
432
+ def dataset_info_attribute_name( # type: ignore[empty-body]
433
+ self, info: DatasetInfoPtr, component_idx: int, attribute_idx: int
434
+ ) -> str:
435
+ pass # pragma: no cover
436
+
437
+ @make_c_binding
438
+ def create_dataset_mutable( # type: ignore[empty-body]
439
+ self, dataset: str, is_batch: int, batch_size: int
440
+ ) -> MutableDatasetPtr:
441
+ pass # pragma: no cover
442
+
443
+ @make_c_binding
444
+ def destroy_dataset_mutable(self, dataset: MutableDatasetPtr) -> None: # type: ignore[empty-body]
445
+ pass # pragma: no cover
446
+
447
+ @make_c_binding
448
+ def dataset_mutable_add_buffer( # type: ignore[empty-body] # noqa: PLR0913
449
+ self,
450
+ dataset: MutableDatasetPtr,
451
+ component: str,
452
+ elements_per_scenario: int,
453
+ total_elements: int,
454
+ indptr: IdxPtr, # type: ignore[valid-type]
455
+ data: VoidPtr, # type: ignore[valid-type]
456
+ ) -> None:
457
+ pass # pragma: no cover
458
+
459
+ @make_c_binding
460
+ def dataset_mutable_add_attribute_buffer(
461
+ self,
462
+ dataset: MutableDatasetPtr,
463
+ component: str,
464
+ attribute: str,
465
+ data: VoidPtr, # type: ignore[valid-type]
466
+ ) -> None: # type: ignore[empty-body]
467
+ pass # pragma: no cover
468
+
469
+ @make_c_binding
470
+ def dataset_mutable_get_info(self, dataset: MutableDatasetPtr) -> DatasetInfoPtr: # type: ignore[empty-body]
471
+ pass # pragma: no cover
472
+
473
+ @make_c_binding
474
+ def create_dataset_const_from_mutable( # type: ignore[empty-body]
475
+ self, mutable_dataset: MutableDatasetPtr
476
+ ) -> ConstDatasetPtr:
477
+ pass # pragma: no cover
478
+
479
+ @make_c_binding
480
+ def destroy_dataset_const(self, dataset: ConstDatasetPtr) -> None: # type: ignore[empty-body]
481
+ pass # pragma: no cover
482
+
483
+ @make_c_binding
484
+ def dataset_const_get_info(self, dataset: ConstDatasetPtr) -> DatasetInfoPtr: # type: ignore[empty-body]
485
+ pass # pragma: no cover
486
+
487
+ @make_c_binding
488
+ def dataset_const_set_next_cartesian_product_dimension(
489
+ self, dataset: ConstDatasetPtr, next_dataset: ConstDatasetPtr
490
+ ) -> None: # type: ignore[empty-body]
491
+ pass # pragma: no cover
492
+
493
+ @make_c_binding
494
+ def dataset_writable_get_info(self, dataset: WritableDatasetPtr) -> DatasetInfoPtr: # type: ignore[empty-body]
495
+ pass # pragma: no cover
496
+
497
+ @make_c_binding
498
+ def dataset_writable_set_buffer(
499
+ self,
500
+ dataset: WritableDatasetPtr,
501
+ component: str,
502
+ indptr: IdxPtr, # type: ignore[valid-type]
503
+ data: VoidPtr, # type: ignore[valid-type]
504
+ ) -> None: # type: ignore[empty-body]
505
+ pass # pragma: no cover
506
+
507
+ @make_c_binding
508
+ def dataset_writable_set_attribute_buffer(
509
+ self,
510
+ dataset: WritableDatasetPtr,
511
+ component: str,
512
+ attribute: str,
513
+ data: VoidPtr, # type: ignore[valid-type]
514
+ ) -> None: # type: ignore[empty-body]
515
+ pass # pragma: no cover
516
+
517
+ @make_c_binding
518
+ def create_deserializer_from_binary_buffer( # type: ignore[empty-body]
519
+ self, data: bytes, size: int, serialization_format: int
520
+ ) -> DeserializerPtr:
521
+ pass # pragma: no cover
522
+
523
+ @make_c_binding
524
+ def create_deserializer_from_null_terminated_string( # type: ignore[empty-body]
525
+ self, data: str, serialization_format: int
526
+ ) -> DeserializerPtr:
527
+ pass # pragma: no cover
528
+
529
+ @make_c_binding
530
+ def deserializer_get_dataset(self, deserializer: DeserializerPtr) -> WritableDatasetPtr: # type: ignore[empty-body]
531
+ pass # pragma: no cover
532
+
533
+ @make_c_binding
534
+ def deserializer_parse_to_buffer(self, deserializer: DeserializerPtr) -> None: # type: ignore[empty-body]
535
+ pass # pragma: no cover
536
+
537
+ @make_c_binding
538
+ def destroy_deserializer(self, deserializer: DeserializerPtr) -> None: # type: ignore[empty-body]
539
+ pass # pragma: no cover
540
+
541
+ @make_c_binding
542
+ def create_serializer( # type: ignore[empty-body]
543
+ self, data: ConstDatasetPtr, serialization_format: int
544
+ ) -> SerializerPtr:
545
+ pass # pragma: no cover
546
+
547
+ @make_c_binding
548
+ def serializer_get_to_binary_buffer( # type: ignore[empty-body]
549
+ self,
550
+ serializer: SerializerPtr,
551
+ use_compact_list: int,
552
+ data: CharDoublePtr, # type: ignore[valid-type]
553
+ size: IdxPtr, # type: ignore[valid-type]
554
+ ) -> None:
555
+ pass # pragma: no cover
556
+
557
+ @make_c_binding
558
+ def serializer_get_to_zero_terminated_string( # type: ignore[empty-body]
559
+ self,
560
+ serializer: SerializerPtr,
561
+ use_compact_list: int,
562
+ indent: int,
563
+ ) -> str:
564
+ pass # pragma: no cover
565
+
566
+ @make_c_binding
567
+ def destroy_serializer(self, serializer: SerializerPtr) -> None: # type: ignore[empty-body]
568
+ pass # pragma: no cover
569
+
570
+
571
+ # make one instance
572
+ def get_power_grid_core() -> PowerGridCore:
573
+ try:
574
+ return _thread_local_data.power_grid_core
575
+ except AttributeError:
576
+ _thread_local_data.power_grid_core = PowerGridCore()
577
+ return _thread_local_data.power_grid_core