power-grid-model 1.11.33__py3-none-win_amd64.whl → 1.12.70__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.
Files changed (67) hide show
  1. power_grid_model/__init__.py +54 -54
  2. power_grid_model/_core/__init__.py +3 -3
  3. power_grid_model/_core/buffer_handling.py +493 -478
  4. power_grid_model/_core/data_handling.py +195 -141
  5. power_grid_model/_core/data_types.py +143 -132
  6. power_grid_model/_core/dataset_definitions.py +109 -108
  7. power_grid_model/_core/enum.py +226 -226
  8. power_grid_model/_core/error_handling.py +206 -205
  9. power_grid_model/_core/errors.py +130 -126
  10. power_grid_model/_core/index_integer.py +17 -17
  11. power_grid_model/_core/options.py +71 -70
  12. power_grid_model/_core/power_grid_core.py +563 -581
  13. power_grid_model/_core/power_grid_dataset.py +535 -534
  14. power_grid_model/_core/power_grid_meta.py +257 -243
  15. power_grid_model/_core/power_grid_model.py +969 -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 +255 -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 +316 -0
  22. power_grid_model/_core/power_grid_model_c/include/power_grid_model_c/dataset_definitions.h +1052 -0
  23. power_grid_model/_core/power_grid_model_c/include/power_grid_model_c/handle.h +99 -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 +125 -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 +220 -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 +317 -317
  47. power_grid_model/_core/typing.py +20 -20
  48. power_grid_model/_core/utils.py +798 -789
  49. power_grid_model/data_types.py +321 -321
  50. power_grid_model/enum.py +27 -27
  51. power_grid_model/errors.py +37 -37
  52. power_grid_model/typing.py +43 -43
  53. power_grid_model/utils.py +473 -469
  54. power_grid_model/validation/__init__.py +25 -25
  55. power_grid_model/validation/_rules.py +1171 -1174
  56. power_grid_model/validation/_validation.py +1172 -1173
  57. power_grid_model/validation/assertions.py +93 -93
  58. power_grid_model/validation/errors.py +602 -589
  59. power_grid_model/validation/utils.py +313 -312
  60. {power_grid_model-1.11.33.dist-info → power_grid_model-1.12.70.dist-info}/METADATA +178 -180
  61. power_grid_model-1.12.70.dist-info/RECORD +65 -0
  62. {power_grid_model-1.11.33.dist-info → power_grid_model-1.12.70.dist-info}/WHEEL +1 -1
  63. power_grid_model-1.12.70.dist-info/entry_points.txt +3 -0
  64. power_grid_model/_core/_power_grid_core.dll +0 -0
  65. power_grid_model-1.11.33.dist-info/RECORD +0 -36
  66. power_grid_model-1.11.33.dist-info/top_level.txt +0 -1
  67. {power_grid_model-1.11.33.dist-info → power_grid_model-1.12.70.dist-info}/licenses/LICENSE +0 -0
@@ -1,687 +1,969 @@
1
- # SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
2
- #
3
- # SPDX-License-Identifier: MPL-2.0
4
-
5
- """
6
- Main power grid model class
7
- """
8
-
9
- from enum import IntEnum
10
- from typing import Type
11
-
12
- import numpy as np
13
-
14
- from power_grid_model._core.data_handling import (
15
- create_output_data,
16
- get_output_type,
17
- prepare_input_view,
18
- prepare_output_view,
19
- prepare_update_view,
20
- )
21
- from power_grid_model._core.data_types import Dataset, SingleDataset
22
- from power_grid_model._core.dataset_definitions import (
23
- ComponentType,
24
- ComponentTypeLike,
25
- _map_to_component_types,
26
- _str_to_component_type,
27
- )
28
- from power_grid_model._core.enum import (
29
- CalculationMethod,
30
- CalculationType,
31
- ShortCircuitVoltageScaling,
32
- TapChangingStrategy,
33
- _ExperimentalFeatures,
34
- )
35
- from power_grid_model._core.error_handling import PowerGridBatchError, assert_no_error, handle_errors
36
- from power_grid_model._core.index_integer import IdNp, IdxNp
37
- from power_grid_model._core.options import Options
38
- from power_grid_model._core.power_grid_core import ConstDatasetPtr, IDPtr, IdxPtr, ModelPtr, power_grid_core as pgc
39
- from power_grid_model._core.typing import ComponentAttributeMapping
40
-
41
-
42
- class PowerGridModel:
43
- """
44
- Main class for Power Grid Model
45
- """
46
-
47
- _model_ptr: ModelPtr
48
- _all_component_count: dict[ComponentType, int] | None
49
- _batch_error: PowerGridBatchError | None
50
-
51
- @property
52
- def batch_error(self) -> PowerGridBatchError | None:
53
- """
54
- Get the batch error object, if present, after a batch calculation with errors.
55
-
56
- Also works when continue_on_batch_error was set to True during the calculation.
57
-
58
- Returns:
59
- Batch error object, or None
60
- """
61
- return self._batch_error
62
-
63
- @property
64
- def _model(self):
65
- if not self._model_ptr:
66
- raise TypeError("You have an empty instance of PowerGridModel!")
67
- return self._model_ptr
68
-
69
- @property
70
- def all_component_count(self) -> dict[ComponentType, int]:
71
- """
72
- Get amount of elements per component type.
73
- If the count for a component type is zero, it will not be in the returned dictionary.
74
-
75
- Returns:
76
- A dictionary with
77
-
78
- - key: Component type name
79
- - value: Integer count of elements of this type
80
- """
81
- if self._all_component_count is None:
82
- raise TypeError("You have an empty instance of PowerGridModel!")
83
- return self._all_component_count
84
-
85
- def copy(self) -> "PowerGridModel":
86
- """
87
- Copy the current model
88
-
89
- Returns:
90
- A copy of PowerGridModel
91
- """
92
- new_model = PowerGridModel.__new__(PowerGridModel)
93
- new_model._model_ptr = pgc.copy_model(self._model)
94
- assert_no_error()
95
- new_model._all_component_count = self._all_component_count
96
- return new_model
97
-
98
- def __copy__(self):
99
- return self.copy()
100
-
101
- def __new__(cls, *_args, **_kwargs):
102
- instance = super().__new__(cls)
103
- instance._model_ptr = ModelPtr()
104
- instance._all_component_count = None
105
- return instance
106
-
107
- def __init__(self, input_data: SingleDataset, system_frequency: float = 50.0):
108
- """
109
- Initialize the model from an input data set.
110
-
111
- Args:
112
- input_data: Input data dictionary
113
-
114
- - key: Component type
115
- - value: Component data with the correct type :class:`SingleComponentData`
116
-
117
- system_frequency: Frequency of the power system, default 50 Hz
118
- """
119
- # destroy old instance
120
- pgc.destroy_model(self._model_ptr)
121
- self._all_component_count = None
122
- # create new
123
- prepared_input = prepare_input_view(_map_to_component_types(input_data))
124
- self._model_ptr = pgc.create_model(system_frequency, input_data=prepared_input.get_dataset_ptr())
125
- assert_no_error()
126
- self._all_component_count = {k: v for k, v in prepared_input.get_info().total_elements().items() if v > 0}
127
-
128
- def update(self, *, update_data: Dataset):
129
- """
130
- Update the model with changes.
131
-
132
- The model will be in an invalid state if the update fails and should be discarded.
133
-
134
- Args:
135
- update_data: Update data dictionary
136
-
137
- - key: Component type
138
- - value: Component data with the correct type :class:`ComponentData` (single scenario or batch)
139
-
140
- Raises:
141
- PowerGridError if the update fails. The model is left in an invalid state and should be discarded.
142
-
143
- Returns:
144
- None
145
- """
146
- prepared_update = prepare_update_view(_map_to_component_types(update_data))
147
- pgc.update_model(self._model, prepared_update.get_dataset_ptr())
148
- assert_no_error()
149
-
150
- def get_indexer(self, component_type: ComponentTypeLike, ids: np.ndarray):
151
- """
152
- Get array of indexers given array of ids for component type.
153
-
154
- This enables syntax like input_data[ComponentType.node][get_indexer(ids)]
155
-
156
- Args:
157
- component_type: Type of component
158
- ids: Array of ids
159
-
160
- Returns:
161
- Array of indexers, same shape as input array ids
162
- """
163
- component_type = _str_to_component_type(component_type)
164
- ids_c = np.ascontiguousarray(ids, dtype=IdNp).ctypes.data_as(IDPtr)
165
- indexer = np.empty_like(ids, dtype=IdxNp, order="C")
166
- indexer_c = indexer.ctypes.data_as(IdxPtr)
167
- size = ids.size
168
- # call c function
169
- pgc.get_indexer(self._model, component_type, size, ids_c, indexer_c)
170
- assert_no_error()
171
- return indexer
172
-
173
- def _get_output_component_count(self, calculation_type: CalculationType):
174
- exclude_types = {
175
- CalculationType.power_flow: [
176
- ComponentType.sym_voltage_sensor,
177
- ComponentType.asym_voltage_sensor,
178
- ComponentType.sym_power_sensor,
179
- ComponentType.asym_power_sensor,
180
- ComponentType.fault,
181
- ],
182
- CalculationType.state_estimation: [ComponentType.fault],
183
- CalculationType.short_circuit: [
184
- ComponentType.sym_voltage_sensor,
185
- ComponentType.asym_voltage_sensor,
186
- ComponentType.sym_power_sensor,
187
- ComponentType.asym_power_sensor,
188
- ],
189
- }.get(calculation_type, [])
190
-
191
- def include_type(component_type: ComponentType):
192
- return all(exclude_type.value not in component_type.value for exclude_type in exclude_types)
193
-
194
- return {ComponentType[k]: v for k, v in self.all_component_count.items() if include_type(k)}
195
-
196
- def _construct_output(
197
- self,
198
- output_component_types: ComponentAttributeMapping,
199
- calculation_type: CalculationType,
200
- symmetric: bool,
201
- is_batch: bool,
202
- batch_size: int,
203
- ) -> dict[ComponentType, np.ndarray]:
204
- all_component_count = self._get_output_component_count(calculation_type=calculation_type)
205
- return create_output_data(
206
- output_component_types=output_component_types,
207
- output_type=get_output_type(calculation_type=calculation_type, symmetric=symmetric),
208
- all_component_count=all_component_count,
209
- is_batch=is_batch,
210
- batch_size=batch_size,
211
- )
212
-
213
- @staticmethod
214
- def _options(**kwargs) -> Options:
215
- def as_enum_value(key_enum: str, type_: Type[IntEnum]):
216
- if key_enum in kwargs:
217
- value_enum = kwargs[key_enum]
218
- if isinstance(value_enum, str):
219
- kwargs[key_enum] = type_[value_enum]
220
-
221
- as_enum_value("calculation_method", CalculationMethod)
222
- as_enum_value("tap_changing_strategy", TapChangingStrategy)
223
- as_enum_value("short_circuit_voltage_scaling", ShortCircuitVoltageScaling)
224
- as_enum_value("experimental_features", _ExperimentalFeatures)
225
-
226
- opt = Options()
227
- for key, value in kwargs.items():
228
- setattr(opt, key, value.value if isinstance(value, IntEnum) else value)
229
- return opt
230
-
231
- def _handle_errors(self, continue_on_batch_error: bool, batch_size: int, decode_error: bool):
232
- self._batch_error = handle_errors(
233
- continue_on_batch_error=continue_on_batch_error,
234
- batch_size=batch_size,
235
- decode_error=decode_error,
236
- )
237
-
238
- def _calculate_impl(
239
- self,
240
- calculation_type: CalculationType,
241
- symmetric: bool,
242
- update_data: Dataset | None,
243
- output_component_types: ComponentAttributeMapping,
244
- options: Options,
245
- continue_on_batch_error: bool,
246
- decode_error: bool,
247
- experimental_features: _ExperimentalFeatures | str, # NOSONAR
248
- ):
249
- """
250
- Core calculation routine
251
-
252
- Args:
253
- calculation_type:
254
- symmetric:
255
- update_data:
256
- output_component_types:
257
- options:
258
- continue_on_batch_error:
259
- decode_error:
260
-
261
- Returns:
262
- """
263
- self._batch_error = None
264
- is_batch = update_data is not None
265
-
266
- if update_data is not None:
267
- prepared_update = prepare_update_view(update_data)
268
- update_ptr = prepared_update.get_dataset_ptr()
269
- batch_size = prepared_update.get_info().batch_size()
270
- else:
271
- update_ptr = ConstDatasetPtr()
272
- batch_size = 1
273
-
274
- output_data = self._construct_output(
275
- output_component_types=output_component_types,
276
- calculation_type=calculation_type,
277
- symmetric=symmetric,
278
- is_batch=is_batch,
279
- batch_size=batch_size,
280
- )
281
- prepared_result = prepare_output_view(
282
- output_data=output_data,
283
- output_type=get_output_type(calculation_type=calculation_type, symmetric=symmetric),
284
- )
285
-
286
- # run calculation
287
- pgc.calculate(
288
- # model and options
289
- self._model,
290
- options.opt,
291
- output_data=prepared_result.get_dataset_ptr(),
292
- update_data=update_ptr,
293
- )
294
-
295
- self._handle_errors(
296
- continue_on_batch_error=continue_on_batch_error,
297
- batch_size=batch_size,
298
- decode_error=decode_error,
299
- )
300
-
301
- return output_data
302
-
303
- def _calculate_power_flow(
304
- self,
305
- *,
306
- symmetric: bool = True,
307
- error_tolerance: float = 1e-8,
308
- max_iterations: int = 20,
309
- calculation_method: CalculationMethod | str = CalculationMethod.newton_raphson,
310
- update_data: Dataset | None = None,
311
- threading: int = -1,
312
- output_component_types: ComponentAttributeMapping = None,
313
- continue_on_batch_error: bool = False,
314
- decode_error: bool = True,
315
- tap_changing_strategy: TapChangingStrategy | str = TapChangingStrategy.disabled,
316
- experimental_features: _ExperimentalFeatures | str = _ExperimentalFeatures.disabled,
317
- ):
318
- calculation_type = CalculationType.power_flow
319
- options = self._options(
320
- calculation_type=calculation_type,
321
- symmetric=symmetric,
322
- error_tolerance=error_tolerance,
323
- max_iterations=max_iterations,
324
- calculation_method=calculation_method,
325
- tap_changing_strategy=tap_changing_strategy,
326
- threading=threading,
327
- experimental_features=experimental_features,
328
- )
329
- return self._calculate_impl(
330
- calculation_type=calculation_type,
331
- symmetric=symmetric,
332
- update_data=update_data,
333
- output_component_types=output_component_types,
334
- options=options,
335
- continue_on_batch_error=continue_on_batch_error,
336
- decode_error=decode_error,
337
- experimental_features=experimental_features,
338
- )
339
-
340
- def _calculate_state_estimation(
341
- self,
342
- *,
343
- symmetric: bool = True,
344
- error_tolerance: float = 1e-8,
345
- max_iterations: int = 20,
346
- calculation_method: CalculationMethod | str = CalculationMethod.iterative_linear,
347
- update_data: Dataset | None = None,
348
- threading: int = -1,
349
- output_component_types: ComponentAttributeMapping = None,
350
- continue_on_batch_error: bool = False,
351
- decode_error: bool = True,
352
- experimental_features: _ExperimentalFeatures | str = _ExperimentalFeatures.disabled,
353
- ) -> dict[ComponentType, np.ndarray]:
354
- calculation_type = CalculationType.state_estimation
355
- options = self._options(
356
- calculation_type=calculation_type,
357
- symmetric=symmetric,
358
- error_tolerance=error_tolerance,
359
- max_iterations=max_iterations,
360
- calculation_method=calculation_method,
361
- threading=threading,
362
- experimental_features=experimental_features,
363
- )
364
- return self._calculate_impl(
365
- calculation_type=calculation_type,
366
- symmetric=symmetric,
367
- update_data=update_data,
368
- output_component_types=output_component_types,
369
- options=options,
370
- continue_on_batch_error=continue_on_batch_error,
371
- decode_error=decode_error,
372
- experimental_features=experimental_features,
373
- )
374
-
375
- def _calculate_short_circuit(
376
- self,
377
- *,
378
- calculation_method: CalculationMethod | str = CalculationMethod.iec60909,
379
- update_data: Dataset | None = None,
380
- threading: int = -1,
381
- output_component_types: ComponentAttributeMapping = None,
382
- continue_on_batch_error: bool = False,
383
- decode_error: bool = True,
384
- short_circuit_voltage_scaling: ShortCircuitVoltageScaling | str = ShortCircuitVoltageScaling.maximum,
385
- experimental_features: _ExperimentalFeatures | str = _ExperimentalFeatures.disabled,
386
- ) -> dict[ComponentType, np.ndarray]:
387
- calculation_type = CalculationType.short_circuit
388
- symmetric = False
389
-
390
- options = self._options(
391
- calculation_type=calculation_type,
392
- symmetric=symmetric,
393
- calculation_method=calculation_method,
394
- threading=threading,
395
- short_circuit_voltage_scaling=short_circuit_voltage_scaling,
396
- experimental_features=experimental_features,
397
- )
398
- return self._calculate_impl(
399
- calculation_type=calculation_type,
400
- symmetric=symmetric,
401
- update_data=update_data,
402
- output_component_types=output_component_types,
403
- options=options,
404
- continue_on_batch_error=continue_on_batch_error,
405
- decode_error=decode_error,
406
- experimental_features=experimental_features,
407
- )
408
-
409
- def calculate_power_flow(
410
- self,
411
- *,
412
- symmetric: bool = True,
413
- error_tolerance: float = 1e-8,
414
- max_iterations: int = 20,
415
- calculation_method: CalculationMethod | str = CalculationMethod.newton_raphson,
416
- update_data: dict[str, np.ndarray | dict[str, np.ndarray]] | Dataset | None = None,
417
- threading: int = -1,
418
- output_component_types: ComponentAttributeMapping = None,
419
- continue_on_batch_error: bool = False,
420
- decode_error: bool = True,
421
- tap_changing_strategy: TapChangingStrategy | str = TapChangingStrategy.disabled,
422
- ) -> dict[ComponentType, np.ndarray]:
423
- """
424
- Calculate power flow once with the current model attributes.
425
- Or calculate in batch with the given update dataset in batch.
426
-
427
- Args:
428
- symmetric (bool, optional): Whether to perform a three-phase symmetric calculation.
429
-
430
- - True: Three-phase symmetric calculation, even for asymmetric loads/generations (Default).
431
- - False: Three-phase asymmetric calculation.
432
- error_tolerance (float, optional): Error tolerance for voltage in p.u., applicable only when the
433
- calculation method is iterative.
434
- max_iterations (int, optional): Maximum number of iterations, applicable only when the calculation method
435
- is iterative.
436
- calculation_method (an enumeration or string): The calculation method to use.
437
-
438
- - newton_raphson: Use Newton-Raphson iterative method (default).
439
- - linear: Use linear method.
440
- update_data (dict, optional):
441
- None: Calculate power flow once with the current model attributes.
442
- Or a dictionary for batch calculation with batch update.
443
-
444
- - key: Component type name to be updated in batch.
445
- - value:
446
-
447
- - For homogeneous update batch (a 2D numpy structured array):
448
-
449
- - Dimension 0: Each batch.
450
- - Dimension 1: Each updated element per batch for this component type.
451
- - For inhomogeneous update batch (a dictionary containing two keys):
452
-
453
- - indptr: A 1D numpy int64 array with length n_batch + 1. Given batch number k, the
454
- update array for this batch is data[indptr[k]:indptr[k + 1]]. This is the concept of
455
- compressed sparse structure.
456
- https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.csr_matrix.html
457
- - data: 1D numpy structured array in flat.
458
- threading (int, optional): Applicable only for batch calculation.
459
-
460
- - < 0: Sequential
461
- - = 0: Parallel, use number of hardware threads
462
- - > 0: Specify number of parallel threads
463
- output_component_types (ComponentAttributeMapping):
464
-
465
- - None: Row based data for all component types.
466
- - set[ComponentTypeVar] or list[ComponentTypeVar]: Row based data for the specified component types.
467
- - ComponentAttributeFilterOptions: Columnar data for all component types.
468
- - dict[ComponentType, set[str] | list[str] | None | ComponentAttributeFilterOptions]:
469
- key: ComponentType
470
- value:
471
- - None: Row based data for the specified component types.
472
- - ComponentAttributeFilterOptions: Columnar data for the specified component types.
473
- - set[str] | list[str]: Columnar data for the specified component types and attributes.
474
- continue_on_batch_error (bool, optional):
475
- Continue the program (instead of throwing error) if some scenarios fail.
476
- You can still retrieve the errors and succeeded/failed scenarios via the batch_error.
477
- decode_error (bool, optional):
478
- Decode error messages to their derived types if possible.
479
-
480
- Returns:
481
- Dictionary of results of all components.
482
-
483
- - key: Component type name to be updated in batch.
484
- - value:
485
-
486
- - For single calculation: 1D numpy structured array for the results of this component type.
487
- - For batch calculation: 2D numpy structured array for the results of this component type.
488
-
489
- - Dimension 0: Each batch.
490
- - Dimension 1: The result of each element for this component type.
491
-
492
- Raises:
493
- Exception: In case an error in the core occurs, an exception will be thrown.
494
- """
495
- return self._calculate_power_flow(
496
- symmetric=symmetric,
497
- error_tolerance=error_tolerance,
498
- max_iterations=max_iterations,
499
- calculation_method=calculation_method,
500
- update_data=(_map_to_component_types(update_data) if update_data is not None else None),
501
- threading=threading,
502
- output_component_types=output_component_types,
503
- continue_on_batch_error=continue_on_batch_error,
504
- decode_error=decode_error,
505
- tap_changing_strategy=tap_changing_strategy,
506
- )
507
-
508
- def calculate_state_estimation(
509
- self,
510
- *,
511
- symmetric: bool = True,
512
- error_tolerance: float = 1e-8,
513
- max_iterations: int = 20,
514
- calculation_method: CalculationMethod | str = CalculationMethod.iterative_linear,
515
- update_data: dict[str, np.ndarray | dict[str, np.ndarray]] | Dataset | None = None,
516
- threading: int = -1,
517
- output_component_types: ComponentAttributeMapping = None,
518
- continue_on_batch_error: bool = False,
519
- decode_error: bool = True,
520
- ) -> dict[ComponentType, np.ndarray]:
521
- """
522
- Calculate state estimation once with the current model attributes.
523
- Or calculate in batch with the given update dataset in batch.
524
-
525
- Args:
526
- symmetric (bool, optional): Whether to perform a three-phase symmetric calculation.
527
-
528
- - True: Three-phase symmetric calculation, even for asymmetric loads/generations (Default).
529
- - False: Three-phase asymmetric calculation.
530
- error_tolerance (float, optional): error tolerance for voltage in p.u., only applicable when the
531
- calculation method is iterative.
532
- max_iterations (int, optional): Maximum number of iterations, applicable only when the calculation method
533
- is iterative.
534
- calculation_method (an enumeration): Use iterative linear method.
535
- update_data (dict, optional):
536
- None: Calculate state estimation once with the current model attributes.
537
- Or a dictionary for batch calculation with batch update.
538
-
539
- - key: Component type name to be updated in batch.
540
- - value:
541
-
542
- - For homogeneous update batch (a 2D numpy structured array):
543
-
544
- - Dimension 0: Each batch.
545
- - Dimension 1: Each updated element per batch for this component type.
546
- - For inhomogeneous update batch (a dictionary containing two keys):
547
-
548
- - indptr: A 1D numpy int64 array with length n_batch + 1. Given batch number k, the
549
- update array for this batch is data[indptr[k]:indptr[k + 1]]. This is the concept of
550
- compressed sparse structure.
551
- https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.csr_matrix.html
552
- - data: 1D numpy structured array in flat.
553
- threading (int, optional): Applicable only for batch calculation.
554
-
555
- - < 0: Sequential
556
- - = 0: Parallel, use number of hardware threads
557
- - > 0: Specify number of parallel threads
558
- output_component_types (ComponentAttributeMapping):
559
-
560
- - None: Row based data for all component types.
561
- - set[ComponentTypeVar] or list[ComponentTypeVar]: Row based data for the specified component types.
562
- - ComponentAttributeFilterOptions: Columnar data for all component types.
563
- - dict[ComponentType, set[str] | list[str] | None | ComponentAttributeFilterOptions]:
564
- key: ComponentType
565
- value:
566
- - None: Row based data for the specified component types.
567
- - ComponentAttributeFilterOptions: Columnar data for the specified component types.
568
- - set[str] | list[str]: Columnar data for the specified component types and attributes.
569
- continue_on_batch_error (bool, optional):
570
- Continue the program (instead of throwing error) if some scenarios fail.
571
- You can still retrieve the errors and succeeded/failed scenarios via the batch_error.
572
- decode_error (bool, optional):
573
- Decode error messages to their derived types if possible.
574
-
575
- Returns:
576
- Dictionary of results of all components.
577
-
578
- - key: Component type name to be updated in batch.
579
- - value:
580
-
581
- - For single calculation: 1D numpy structured array for the results of this component type.
582
- - For batch calculation: 2D numpy structured array for the results of this component type.
583
-
584
- - Dimension 0: Each batch.
585
- - Dimension 1: The result of each element for this component type.
586
-
587
- Raises:
588
- Exception: In case an error in the core occurs, an exception will be thrown.
589
- """
590
- return self._calculate_state_estimation(
591
- symmetric=symmetric,
592
- error_tolerance=error_tolerance,
593
- max_iterations=max_iterations,
594
- calculation_method=calculation_method,
595
- update_data=(_map_to_component_types(update_data) if update_data is not None else None),
596
- threading=threading,
597
- output_component_types=output_component_types,
598
- continue_on_batch_error=continue_on_batch_error,
599
- decode_error=decode_error,
600
- )
601
-
602
- def calculate_short_circuit(
603
- self,
604
- *,
605
- calculation_method: CalculationMethod | str = CalculationMethod.iec60909,
606
- update_data: dict[str, np.ndarray | dict[str, np.ndarray]] | Dataset | None = None,
607
- threading: int = -1,
608
- output_component_types: ComponentAttributeMapping = None,
609
- continue_on_batch_error: bool = False,
610
- decode_error: bool = True,
611
- short_circuit_voltage_scaling: ShortCircuitVoltageScaling | str = ShortCircuitVoltageScaling.maximum,
612
- ) -> dict[ComponentType, np.ndarray]:
613
- """
614
- Calculate a short circuit once with the current model attributes.
615
- Or calculate in batch with the given update dataset in batch
616
-
617
- Args:
618
- calculation_method (an enumeration): Use the iec60909 standard.
619
- update_data:
620
- None: calculate a short circuit once with the current model attributes.
621
- Or a dictionary for batch calculation with batch update
622
-
623
- - key: Component type name to be updated in batch
624
- - value:
625
-
626
- - For homogeneous update batch (a 2D numpy structured array):
627
-
628
- - Dimension 0: each batch
629
- - Dimension 1: each updated element per batch for this component type
630
- - For inhomogeneous update batch (a dictionary containing two keys):
631
-
632
- - indptr: A 1D numpy int64 array with length n_batch + 1. Given batch number k, the
633
- update array for this batch is data[indptr[k]:indptr[k + 1]]. This is the concept of
634
- compressed sparse structure.
635
- https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.csr_matrix.html
636
- - data: 1D numpy structured array in flat.
637
- threading (int, optional): Applicable only for batch calculation.
638
-
639
- - < 0: Sequential
640
- - = 0: Parallel, use number of hardware threads
641
- - > 0: Specify number of parallel threads
642
- output_component_types (ComponentAttributeMapping):
643
-
644
- - None: Row based data for all component types.
645
- - set[ComponentTypeVar] or list[ComponentTypeVar]: Row based data for the specified component types.
646
- - ComponentAttributeFilterOptions: Columnar data for all component types.
647
- - dict[ComponentType, set[str] | list[str] | None | ComponentAttributeFilterOptions]:
648
- key: ComponentType
649
- value:
650
- - None: Row based data for the specified component types.
651
- - ComponentAttributeFilterOptions: Columnar data for the specified component types.
652
- - set[str] | list[str]: Columnar data for the specified component types and attributes.
653
- continue_on_batch_error (bool, optional):
654
- Continue the program (instead of throwing error) if some scenarios fail.
655
- You can still retrieve the errors and succeeded/failed scenarios via the batch_error.
656
- decode_error (bool, optional):
657
- Decode error messages to their derived types if possible.
658
- short_circuit_voltage_scaling ({ShortCircuitVoltageSaling, str}, optional):
659
- Whether to use the maximum or minimum voltage scaling.
660
- By default, the maximum voltage scaling is used to calculate the short circuit.
661
-
662
- Returns:
663
- Dictionary of results of all components.
664
-
665
- - key: Component type name to be updated in batch.
666
- - value:
667
-
668
- - For single calculation: 1D numpy structured array for the results of this component type.
669
- - For batch calculation: 2D numpy structured array for the results of this component type.
670
-
671
- - Dimension 0: Each batch.
672
- - Dimension 1: The result of each element for this component type.
673
- Raises:
674
- Exception: In case an error in the core occurs, an exception will be thrown.
675
- """
676
- return self._calculate_short_circuit(
677
- calculation_method=calculation_method,
678
- update_data=(_map_to_component_types(update_data) if update_data is not None else None),
679
- threading=threading,
680
- output_component_types=output_component_types,
681
- continue_on_batch_error=continue_on_batch_error,
682
- decode_error=decode_error,
683
- short_circuit_voltage_scaling=short_circuit_voltage_scaling,
684
- )
685
-
686
- def __del__(self):
687
- pgc.destroy_model(self._model_ptr)
1
+ # SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
2
+ #
3
+ # SPDX-License-Identifier: MPL-2.0
4
+
5
+ """
6
+ Main power grid model class
7
+ """
8
+
9
+ from enum import IntEnum
10
+ from typing import Any, overload
11
+
12
+ import numpy as np
13
+
14
+ from power_grid_model._core.data_handling import (
15
+ create_output_data,
16
+ get_output_type,
17
+ prepare_input_view,
18
+ prepare_output_view,
19
+ prepare_update_view,
20
+ )
21
+ from power_grid_model._core.data_types import (
22
+ BatchDataset,
23
+ Dataset,
24
+ DenseBatchColumnarOutputDataset,
25
+ DenseBatchOutputDataset,
26
+ DenseBatchRowBasedOutputDataset,
27
+ SingleColumnarOutputDataset,
28
+ SingleDataset,
29
+ SingleOutputDataset,
30
+ SingleRowBasedDataset,
31
+ SingleRowBasedOutputDataset,
32
+ )
33
+ from power_grid_model._core.dataset_definitions import (
34
+ ComponentType,
35
+ ComponentTypeLike,
36
+ ComponentTypeVar,
37
+ _map_to_component_types,
38
+ _str_to_component_type,
39
+ )
40
+ from power_grid_model._core.enum import (
41
+ CalculationMethod,
42
+ CalculationType,
43
+ ComponentAttributeFilterOptions,
44
+ ShortCircuitVoltageScaling,
45
+ TapChangingStrategy,
46
+ _ExperimentalFeatures,
47
+ )
48
+ from power_grid_model._core.error_handling import PowerGridBatchError, assert_no_error, handle_errors
49
+ from power_grid_model._core.index_integer import IdNp, IdxNp
50
+ from power_grid_model._core.options import Options
51
+ from power_grid_model._core.power_grid_core import ConstDatasetPtr, IDPtr, IdxPtr, ModelPtr, power_grid_core as pgc
52
+ from power_grid_model._core.typing import ComponentAttributeMapping, ComponentAttributeMappingDict
53
+
54
+
55
+ class PowerGridModel:
56
+ """
57
+ Main class for Power Grid Model
58
+ """
59
+
60
+ _model_ptr: ModelPtr
61
+ _all_component_count: dict[ComponentType, int] | None
62
+ _batch_error: PowerGridBatchError | None
63
+
64
+ @property
65
+ def batch_error(self) -> PowerGridBatchError | None:
66
+ """
67
+ Get the batch error object, if present, after a batch calculation with errors.
68
+
69
+ Also works when continue_on_batch_error was set to True during the calculation.
70
+
71
+ Returns:
72
+ Batch error object, or None
73
+ """
74
+ return self._batch_error
75
+
76
+ @property
77
+ def _model(self):
78
+ if not self._model_ptr:
79
+ raise TypeError("You have an empty instance of PowerGridModel!")
80
+ return self._model_ptr
81
+
82
+ @property
83
+ def all_component_count(self) -> dict[ComponentType, int]:
84
+ """
85
+ Get amount of elements per component type.
86
+ If the count for a component type is zero, it will not be in the returned dictionary.
87
+
88
+ Returns:
89
+ A dictionary with
90
+
91
+ - key: Component type name
92
+ - value: Integer count of elements of this type
93
+ """
94
+ if self._all_component_count is None:
95
+ raise TypeError("You have an empty instance of PowerGridModel!")
96
+ return self._all_component_count
97
+
98
+ def copy(self) -> "PowerGridModel":
99
+ """
100
+ Copy the current model
101
+
102
+ Returns:
103
+ A copy of PowerGridModel
104
+ """
105
+ new_model = PowerGridModel.__new__(PowerGridModel)
106
+ new_model._model_ptr = pgc.copy_model(self._model)
107
+ assert_no_error()
108
+ new_model._all_component_count = self._all_component_count
109
+ return new_model
110
+
111
+ def __copy__(self) -> "PowerGridModel":
112
+ return self.copy()
113
+
114
+ def __deepcopy__(self, memo: dict[int, Any]) -> "PowerGridModel":
115
+ # PowerGridModel.copy makes already a deepcopy
116
+ new_model = self.copy()
117
+
118
+ # memorize that this object (self) has been deepcopied
119
+ memo[id(self)] = new_model
120
+
121
+ return new_model
122
+
123
+ def __new__(cls, *_args, **_kwargs):
124
+ instance = super().__new__(cls)
125
+ instance._model_ptr = ModelPtr()
126
+ instance._all_component_count = None
127
+ return instance
128
+
129
+ def __init__(self, input_data: SingleDataset, system_frequency: float = 50.0):
130
+ """
131
+ Initialize the model from an input data set.
132
+
133
+ Args:
134
+ input_data: Input data dictionary
135
+
136
+ - key: Component type
137
+ - value: Component data with the correct type :class:`SingleComponentData`
138
+
139
+ system_frequency: Frequency of the power system, default 50 Hz
140
+ """
141
+ # destroy old instance
142
+ pgc.destroy_model(self._model_ptr)
143
+ self._all_component_count = None
144
+ # create new
145
+ prepared_input = prepare_input_view(_map_to_component_types(input_data))
146
+ self._model_ptr = pgc.create_model(system_frequency, input_data=prepared_input.get_dataset_ptr())
147
+ assert_no_error()
148
+ self._all_component_count = {k: v for k, v in prepared_input.get_info().total_elements().items() if v > 0}
149
+
150
+ def update(self, *, update_data: Dataset):
151
+ """
152
+ Update the model with changes.
153
+
154
+ The model will be in an invalid state if the update fails and should be discarded.
155
+
156
+ Args:
157
+ update_data: Update data dictionary
158
+
159
+ - key: Component type
160
+ - value: Component data with the correct type :class:`ComponentData` (single scenario or batch)
161
+
162
+ Raises:
163
+ PowerGridError if the update fails. The model is left in an invalid state and should be discarded.
164
+
165
+ Returns:
166
+ None
167
+ """
168
+ prepared_update = prepare_update_view(_map_to_component_types(update_data))
169
+ pgc.update_model(self._model, prepared_update.get_dataset_ptr())
170
+ assert_no_error()
171
+
172
+ def get_indexer(self, component_type: ComponentTypeLike, ids: np.ndarray):
173
+ """
174
+ Get array of indexers given array of ids for component type.
175
+
176
+ This enables syntax like input_data[ComponentType.node][get_indexer(ids)]
177
+
178
+ Args:
179
+ component_type: Type of component
180
+ ids: Array of ids
181
+
182
+ Returns:
183
+ Array of indexers, same shape as input array ids
184
+ """
185
+ component_type = _str_to_component_type(component_type)
186
+ ids_c = np.ascontiguousarray(ids, dtype=IdNp).ctypes.data_as(IDPtr)
187
+ indexer = np.empty_like(ids, dtype=IdxNp, order="C")
188
+ indexer_c = indexer.ctypes.data_as(IdxPtr)
189
+ size = ids.size
190
+ # call c function
191
+ pgc.get_indexer(self._model, component_type, size, ids_c, indexer_c)
192
+ assert_no_error()
193
+ return indexer
194
+
195
+ def _get_output_component_count(self, calculation_type: CalculationType):
196
+ exclude_types = {
197
+ CalculationType.power_flow: [
198
+ ComponentType.sym_voltage_sensor,
199
+ ComponentType.asym_voltage_sensor,
200
+ ComponentType.sym_power_sensor,
201
+ ComponentType.asym_power_sensor,
202
+ ComponentType.fault,
203
+ ],
204
+ CalculationType.state_estimation: [ComponentType.fault],
205
+ CalculationType.short_circuit: [
206
+ ComponentType.sym_voltage_sensor,
207
+ ComponentType.asym_voltage_sensor,
208
+ ComponentType.sym_power_sensor,
209
+ ComponentType.asym_power_sensor,
210
+ ],
211
+ }.get(calculation_type, [])
212
+
213
+ def include_type(component_type: ComponentType):
214
+ return all(exclude_type.value not in component_type.value for exclude_type in exclude_types)
215
+
216
+ return {ComponentType[k]: v for k, v in self.all_component_count.items() if include_type(k)}
217
+
218
+ def _construct_output(
219
+ self,
220
+ output_component_types: ComponentAttributeMapping,
221
+ calculation_type: CalculationType,
222
+ symmetric: bool,
223
+ is_batch: bool,
224
+ batch_size: int,
225
+ ):
226
+ all_component_count = self._get_output_component_count(calculation_type=calculation_type)
227
+ return create_output_data(
228
+ output_component_types=output_component_types,
229
+ output_type=get_output_type(calculation_type=calculation_type, symmetric=symmetric),
230
+ all_component_count=all_component_count,
231
+ is_batch=is_batch,
232
+ batch_size=batch_size,
233
+ )
234
+
235
+ @staticmethod
236
+ def _options(**kwargs) -> Options:
237
+ def as_enum_value(key_enum: str, type_: type[IntEnum]):
238
+ if key_enum in kwargs:
239
+ value_enum = kwargs[key_enum]
240
+ if isinstance(value_enum, str):
241
+ kwargs[key_enum] = type_[value_enum] # NOSONAR(S5864) IntEnum has __getitem__
242
+
243
+ as_enum_value("calculation_method", CalculationMethod)
244
+ as_enum_value("tap_changing_strategy", TapChangingStrategy)
245
+ as_enum_value("short_circuit_voltage_scaling", ShortCircuitVoltageScaling)
246
+ as_enum_value("experimental_features", _ExperimentalFeatures)
247
+
248
+ opt = Options()
249
+ for key, value in kwargs.items():
250
+ setattr(opt, key, value.value if isinstance(value, IntEnum) else value)
251
+ return opt
252
+
253
+ def _handle_errors(self, continue_on_batch_error: bool, batch_size: int, decode_error: bool):
254
+ self._batch_error = handle_errors(
255
+ continue_on_batch_error=continue_on_batch_error,
256
+ batch_size=batch_size,
257
+ decode_error=decode_error,
258
+ )
259
+
260
+ def _calculate_impl( # noqa: PLR0913
261
+ self,
262
+ calculation_type: CalculationType,
263
+ symmetric: bool,
264
+ update_data: Dataset | None,
265
+ output_component_types: ComponentAttributeMapping,
266
+ options: Options,
267
+ continue_on_batch_error: bool,
268
+ decode_error: bool,
269
+ experimental_features: _ExperimentalFeatures | str, # NOSONAR # noqa: ARG002
270
+ ) -> Dataset:
271
+ """
272
+ Core calculation routine
273
+
274
+ Args:
275
+ calculation_type:
276
+ symmetric:
277
+ update_data:
278
+ output_component_types:
279
+ options:
280
+ continue_on_batch_error:
281
+ decode_error:
282
+
283
+ Returns:
284
+ """
285
+ self._batch_error = None
286
+ is_batch = update_data is not None
287
+
288
+ if update_data is not None:
289
+ prepared_update = prepare_update_view(update_data)
290
+ update_ptr = prepared_update.get_dataset_ptr()
291
+ batch_size = prepared_update.get_info().batch_size()
292
+ else:
293
+ update_ptr = ConstDatasetPtr()
294
+ batch_size = 1
295
+
296
+ output_data = self._construct_output(
297
+ output_component_types=output_component_types,
298
+ calculation_type=calculation_type,
299
+ symmetric=symmetric,
300
+ is_batch=is_batch,
301
+ batch_size=batch_size,
302
+ )
303
+ prepared_result = prepare_output_view(
304
+ output_data=output_data,
305
+ output_type=get_output_type(calculation_type=calculation_type, symmetric=symmetric),
306
+ )
307
+
308
+ # run calculation
309
+ pgc.calculate(
310
+ # model and options
311
+ self._model,
312
+ options.opt,
313
+ output_data=prepared_result.get_dataset_ptr(),
314
+ update_data=update_ptr,
315
+ )
316
+
317
+ self._handle_errors(
318
+ continue_on_batch_error=continue_on_batch_error,
319
+ batch_size=batch_size,
320
+ decode_error=decode_error,
321
+ )
322
+
323
+ return output_data
324
+
325
+ def _calculate_power_flow( # noqa: PLR0913
326
+ self,
327
+ *,
328
+ symmetric: bool = True,
329
+ error_tolerance: float = 1e-8,
330
+ max_iterations: int = 20,
331
+ calculation_method: CalculationMethod | str = CalculationMethod.newton_raphson,
332
+ update_data: Dataset | None = None,
333
+ threading: int = -1,
334
+ output_component_types: ComponentAttributeMapping = None,
335
+ continue_on_batch_error: bool = False,
336
+ decode_error: bool = True,
337
+ tap_changing_strategy: TapChangingStrategy | str = TapChangingStrategy.disabled,
338
+ experimental_features: _ExperimentalFeatures | str = _ExperimentalFeatures.disabled,
339
+ ) -> Dataset:
340
+ calculation_type = CalculationType.power_flow
341
+ options = self._options(
342
+ calculation_type=calculation_type,
343
+ symmetric=symmetric,
344
+ error_tolerance=error_tolerance,
345
+ max_iterations=max_iterations,
346
+ calculation_method=calculation_method,
347
+ tap_changing_strategy=tap_changing_strategy,
348
+ threading=threading,
349
+ experimental_features=experimental_features,
350
+ )
351
+ return self._calculate_impl(
352
+ calculation_type=calculation_type,
353
+ symmetric=symmetric,
354
+ update_data=update_data,
355
+ output_component_types=output_component_types,
356
+ options=options,
357
+ continue_on_batch_error=continue_on_batch_error,
358
+ decode_error=decode_error,
359
+ experimental_features=experimental_features,
360
+ )
361
+
362
+ def _calculate_state_estimation( # noqa: PLR0913
363
+ self,
364
+ *,
365
+ symmetric: bool = True,
366
+ error_tolerance: float = 1e-8,
367
+ max_iterations: int = 20,
368
+ calculation_method: CalculationMethod | str = CalculationMethod.iterative_linear,
369
+ update_data: Dataset | None = None,
370
+ threading: int = -1,
371
+ output_component_types: ComponentAttributeMapping = None,
372
+ continue_on_batch_error: bool = False,
373
+ decode_error: bool = True,
374
+ experimental_features: _ExperimentalFeatures | str = _ExperimentalFeatures.disabled,
375
+ ) -> Dataset:
376
+ calculation_type = CalculationType.state_estimation
377
+ options = self._options(
378
+ calculation_type=calculation_type,
379
+ symmetric=symmetric,
380
+ error_tolerance=error_tolerance,
381
+ max_iterations=max_iterations,
382
+ calculation_method=calculation_method,
383
+ threading=threading,
384
+ experimental_features=experimental_features,
385
+ )
386
+ return self._calculate_impl(
387
+ calculation_type=calculation_type,
388
+ symmetric=symmetric,
389
+ update_data=update_data,
390
+ output_component_types=output_component_types,
391
+ options=options,
392
+ continue_on_batch_error=continue_on_batch_error,
393
+ decode_error=decode_error,
394
+ experimental_features=experimental_features,
395
+ )
396
+
397
+ def _calculate_short_circuit( # noqa: PLR0913
398
+ self,
399
+ *,
400
+ calculation_method: CalculationMethod | str = CalculationMethod.iec60909,
401
+ update_data: Dataset | None = None,
402
+ threading: int = -1,
403
+ output_component_types: ComponentAttributeMapping = None,
404
+ continue_on_batch_error: bool = False,
405
+ decode_error: bool = True,
406
+ short_circuit_voltage_scaling: ShortCircuitVoltageScaling | str = ShortCircuitVoltageScaling.maximum,
407
+ experimental_features: _ExperimentalFeatures | str = _ExperimentalFeatures.disabled,
408
+ ) -> Dataset:
409
+ calculation_type = CalculationType.short_circuit
410
+ symmetric = False
411
+
412
+ options = self._options(
413
+ calculation_type=calculation_type,
414
+ symmetric=symmetric,
415
+ calculation_method=calculation_method,
416
+ threading=threading,
417
+ short_circuit_voltage_scaling=short_circuit_voltage_scaling,
418
+ experimental_features=experimental_features,
419
+ )
420
+ return self._calculate_impl(
421
+ calculation_type=calculation_type,
422
+ symmetric=symmetric,
423
+ update_data=update_data,
424
+ output_component_types=output_component_types,
425
+ options=options,
426
+ continue_on_batch_error=continue_on_batch_error,
427
+ decode_error=decode_error,
428
+ experimental_features=experimental_features,
429
+ )
430
+
431
+ @overload
432
+ def calculate_power_flow(
433
+ self,
434
+ *,
435
+ symmetric: bool = ...,
436
+ error_tolerance: float = ...,
437
+ max_iterations: int = ...,
438
+ calculation_method: CalculationMethod | str = ...,
439
+ threading: int = ...,
440
+ output_component_types: None | set[ComponentTypeVar] | list[ComponentTypeVar] = ...,
441
+ continue_on_batch_error: bool = ...,
442
+ decode_error: bool = ...,
443
+ tap_changing_strategy: TapChangingStrategy | str = ...,
444
+ ) -> SingleRowBasedDataset: ...
445
+ @overload
446
+ def calculate_power_flow(
447
+ self,
448
+ *,
449
+ symmetric: bool = ...,
450
+ error_tolerance: float = ...,
451
+ max_iterations: int = ...,
452
+ calculation_method: CalculationMethod | str = ...,
453
+ update_data: None = ...,
454
+ threading: int = ...,
455
+ output_component_types: None | set[ComponentTypeVar] | list[ComponentTypeVar] = ...,
456
+ continue_on_batch_error: bool = ...,
457
+ decode_error: bool = ...,
458
+ tap_changing_strategy: TapChangingStrategy | str = ...,
459
+ ) -> SingleRowBasedDataset: ...
460
+ @overload
461
+ def calculate_power_flow(
462
+ self,
463
+ *,
464
+ symmetric: bool = ...,
465
+ error_tolerance: float = ...,
466
+ max_iterations: int = ...,
467
+ calculation_method: CalculationMethod | str = ...,
468
+ update_data: None = ...,
469
+ threading: int = ...,
470
+ output_component_types: ComponentAttributeFilterOptions = ...,
471
+ continue_on_batch_error: bool = ...,
472
+ decode_error: bool = ...,
473
+ tap_changing_strategy: TapChangingStrategy | str = ...,
474
+ ) -> SingleColumnarOutputDataset: ...
475
+ @overload
476
+ def calculate_power_flow(
477
+ self,
478
+ *,
479
+ symmetric: bool = ...,
480
+ error_tolerance: float = ...,
481
+ max_iterations: int = ...,
482
+ calculation_method: CalculationMethod | str = ...,
483
+ update_data: None = ...,
484
+ threading: int = ...,
485
+ output_component_types: ComponentAttributeMappingDict = ...,
486
+ continue_on_batch_error: bool = ...,
487
+ decode_error: bool = ...,
488
+ tap_changing_strategy: TapChangingStrategy | str = ...,
489
+ ) -> SingleOutputDataset: ...
490
+ @overload
491
+ def calculate_power_flow(
492
+ self,
493
+ *,
494
+ symmetric: bool = ...,
495
+ error_tolerance: float = ...,
496
+ max_iterations: int = ...,
497
+ calculation_method: CalculationMethod | str = ...,
498
+ update_data: BatchDataset = ...,
499
+ threading: int = ...,
500
+ output_component_types: None | set[ComponentTypeVar] | list[ComponentTypeVar] = ...,
501
+ continue_on_batch_error: bool = ...,
502
+ decode_error: bool = ...,
503
+ tap_changing_strategy: TapChangingStrategy | str = ...,
504
+ ) -> DenseBatchRowBasedOutputDataset: ...
505
+ @overload
506
+ def calculate_power_flow(
507
+ self,
508
+ *,
509
+ symmetric: bool = ...,
510
+ error_tolerance: float = ...,
511
+ max_iterations: int = ...,
512
+ calculation_method: CalculationMethod | str = ...,
513
+ update_data: BatchDataset = ...,
514
+ threading: int = ...,
515
+ output_component_types: ComponentAttributeFilterOptions = ...,
516
+ continue_on_batch_error: bool = ...,
517
+ decode_error: bool = ...,
518
+ tap_changing_strategy: TapChangingStrategy | str = ...,
519
+ ) -> DenseBatchColumnarOutputDataset: ...
520
+ @overload
521
+ def calculate_power_flow(
522
+ self,
523
+ *,
524
+ symmetric: bool = ...,
525
+ error_tolerance: float = ...,
526
+ max_iterations: int = ...,
527
+ calculation_method: CalculationMethod | str = ...,
528
+ update_data: BatchDataset = ...,
529
+ threading: int = ...,
530
+ output_component_types: ComponentAttributeMappingDict = ...,
531
+ continue_on_batch_error: bool = ...,
532
+ decode_error: bool = ...,
533
+ tap_changing_strategy: TapChangingStrategy | str = ...,
534
+ ) -> DenseBatchOutputDataset: ...
535
+ def calculate_power_flow( # noqa: PLR0913
536
+ self,
537
+ *,
538
+ symmetric: bool = True,
539
+ error_tolerance: float = 1e-8,
540
+ max_iterations: int = 20,
541
+ calculation_method: CalculationMethod | str = CalculationMethod.newton_raphson,
542
+ update_data: BatchDataset | None = None,
543
+ threading: int = -1,
544
+ output_component_types: ComponentAttributeMapping = None,
545
+ continue_on_batch_error: bool = False,
546
+ decode_error: bool = True,
547
+ tap_changing_strategy: TapChangingStrategy | str = TapChangingStrategy.disabled,
548
+ ) -> Dataset:
549
+ """
550
+ Calculate power flow once with the current model attributes.
551
+ Or calculate in batch with the given update dataset in batch.
552
+
553
+ Args:
554
+ symmetric (bool, optional): Whether to perform a three-phase symmetric calculation.
555
+
556
+ - True: Three-phase symmetric calculation, even for asymmetric loads/generations (Default).
557
+ - False: Three-phase asymmetric calculation.
558
+ error_tolerance (float, optional): Error tolerance for voltage in p.u., applicable only when the
559
+ calculation method is iterative.
560
+ max_iterations (int, optional): Maximum number of iterations, applicable only when the calculation method
561
+ is iterative.
562
+ calculation_method (an enumeration or string): The calculation method to use.
563
+
564
+ - newton_raphson: Use Newton-Raphson iterative method (default).
565
+ - linear: Use linear method.
566
+ update_data (dict, optional):
567
+ None: Calculate power flow once with the current model attributes.
568
+ Or a dictionary for batch calculation with batch update.
569
+
570
+ - key: Component type name to be updated in batch.
571
+ - value:
572
+
573
+ - For homogeneous update batch (a 2D numpy structured array):
574
+
575
+ - Dimension 0: Each batch.
576
+ - Dimension 1: Each updated element per batch for this component type.
577
+ - For inhomogeneous update batch (a dictionary containing two keys):
578
+
579
+ - indptr: A 1D numpy int64 array with length n_batch + 1. Given batch number k, the
580
+ update array for this batch is data[indptr[k]:indptr[k + 1]]. This is the concept of
581
+ compressed sparse structure.
582
+ https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.csr_matrix.html
583
+ - data: 1D numpy structured array in flat.
584
+ threading (int, optional): Applicable only for batch calculation.
585
+
586
+ - < 0: Sequential
587
+ - = 0: Parallel, use number of hardware threads
588
+ - > 0: Specify number of parallel threads
589
+ output_component_types (ComponentAttributeMapping):
590
+
591
+ - None: Row based data for all component types.
592
+ - set[ComponentTypeVar] or list[ComponentTypeVar]: Row based data for the specified component types.
593
+ - ComponentAttributeFilterOptions: Columnar data for all component types.
594
+ - ComponentAttributeMappingDict:
595
+ key: ComponentType
596
+ value:
597
+ - None: Row based data for the specified component types.
598
+ - ComponentAttributeFilterOptions: Columnar data for the specified component types.
599
+ - set[str] | list[str]: Columnar data for the specified component types and attributes.
600
+ continue_on_batch_error (bool, optional):
601
+ Continue the program (instead of throwing error) if some scenarios fail.
602
+ You can still retrieve the errors and succeeded/failed scenarios via the batch_error.
603
+ decode_error (bool, optional):
604
+ Decode error messages to their derived types if possible.
605
+
606
+ Returns:
607
+ Dictionary of results of all components.
608
+
609
+ - key: Component type name to be updated in batch.
610
+ - value:
611
+
612
+ - For single calculation: 1D numpy structured array for the results of this component type.
613
+ - For batch calculation: 2D numpy structured array for the results of this component type.
614
+
615
+ - Dimension 0: Each batch.
616
+ - Dimension 1: The result of each element for this component type.
617
+
618
+ Raises:
619
+ Exception: In case an error in the core occurs, an exception will be thrown.
620
+ """
621
+ return self._calculate_power_flow(
622
+ symmetric=symmetric,
623
+ error_tolerance=error_tolerance,
624
+ max_iterations=max_iterations,
625
+ calculation_method=calculation_method,
626
+ update_data=(_map_to_component_types(update_data) if update_data is not None else None),
627
+ threading=threading,
628
+ output_component_types=output_component_types,
629
+ continue_on_batch_error=continue_on_batch_error,
630
+ decode_error=decode_error,
631
+ tap_changing_strategy=tap_changing_strategy,
632
+ )
633
+
634
+ @overload
635
+ def calculate_state_estimation(
636
+ self,
637
+ *,
638
+ symmetric: bool = ...,
639
+ error_tolerance: float = ...,
640
+ max_iterations: int = ...,
641
+ calculation_method: CalculationMethod | str = ...,
642
+ update_data: None = ...,
643
+ threading: int = ...,
644
+ output_component_types: None | set[ComponentTypeVar] | list[ComponentTypeVar] = ...,
645
+ continue_on_batch_error: bool = ...,
646
+ decode_error: bool = ...,
647
+ ) -> SingleRowBasedOutputDataset: ...
648
+ @overload
649
+ def calculate_state_estimation(
650
+ self,
651
+ *,
652
+ symmetric: bool = ...,
653
+ error_tolerance: float = ...,
654
+ max_iterations: int = ...,
655
+ calculation_method: CalculationMethod | str = ...,
656
+ update_data: None = ...,
657
+ threading: int = ...,
658
+ output_component_types: ComponentAttributeFilterOptions = ...,
659
+ continue_on_batch_error: bool = ...,
660
+ decode_error: bool = ...,
661
+ ) -> SingleColumnarOutputDataset: ...
662
+ @overload
663
+ def calculate_state_estimation(
664
+ self,
665
+ *,
666
+ symmetric: bool = ...,
667
+ error_tolerance: float = ...,
668
+ max_iterations: int = ...,
669
+ calculation_method: CalculationMethod | str = ...,
670
+ update_data: None = ...,
671
+ threading: int = ...,
672
+ output_component_types: ComponentAttributeMappingDict = ...,
673
+ continue_on_batch_error: bool = ...,
674
+ decode_error: bool = ...,
675
+ ) -> SingleOutputDataset: ...
676
+ @overload
677
+ def calculate_state_estimation(
678
+ self,
679
+ *,
680
+ symmetric: bool = ...,
681
+ error_tolerance: float = ...,
682
+ max_iterations: int = ...,
683
+ calculation_method: CalculationMethod | str = ...,
684
+ update_data: BatchDataset = ...,
685
+ threading: int = ...,
686
+ output_component_types: None | set[ComponentTypeVar] | list[ComponentTypeVar] = ...,
687
+ continue_on_batch_error: bool = ...,
688
+ decode_error: bool = ...,
689
+ ) -> DenseBatchRowBasedOutputDataset: ...
690
+ @overload
691
+ def calculate_state_estimation(
692
+ self,
693
+ *,
694
+ symmetric: bool = ...,
695
+ error_tolerance: float = ...,
696
+ max_iterations: int = ...,
697
+ calculation_method: CalculationMethod | str = ...,
698
+ update_data: BatchDataset = ...,
699
+ threading: int = ...,
700
+ output_component_types: ComponentAttributeFilterOptions = ...,
701
+ continue_on_batch_error: bool = ...,
702
+ decode_error: bool = ...,
703
+ ) -> DenseBatchColumnarOutputDataset: ...
704
+ @overload
705
+ def calculate_state_estimation(
706
+ self,
707
+ *,
708
+ symmetric: bool = ...,
709
+ error_tolerance: float = ...,
710
+ max_iterations: int = ...,
711
+ calculation_method: CalculationMethod | str = ...,
712
+ update_data: BatchDataset = ...,
713
+ threading: int = ...,
714
+ output_component_types: ComponentAttributeMappingDict = ...,
715
+ continue_on_batch_error: bool = ...,
716
+ decode_error: bool = ...,
717
+ ) -> DenseBatchOutputDataset: ...
718
+ def calculate_state_estimation( # noqa: PLR0913
719
+ self,
720
+ *,
721
+ symmetric: bool = True,
722
+ error_tolerance: float = 1e-8,
723
+ max_iterations: int = 20,
724
+ calculation_method: CalculationMethod | str = CalculationMethod.iterative_linear,
725
+ update_data: BatchDataset | None = None,
726
+ threading: int = -1,
727
+ output_component_types: ComponentAttributeMapping = None,
728
+ continue_on_batch_error: bool = False,
729
+ decode_error: bool = True,
730
+ ) -> Dataset:
731
+ """
732
+ Calculate state estimation once with the current model attributes.
733
+ Or calculate in batch with the given update dataset in batch.
734
+
735
+ Args:
736
+ symmetric (bool, optional): Whether to perform a three-phase symmetric calculation.
737
+
738
+ - True: Three-phase symmetric calculation, even for asymmetric loads/generations (Default).
739
+ - False: Three-phase asymmetric calculation.
740
+ error_tolerance (float, optional): error tolerance for voltage in p.u., only applicable when the
741
+ calculation method is iterative.
742
+ max_iterations (int, optional): Maximum number of iterations, applicable only when the calculation method
743
+ is iterative.
744
+ calculation_method (an enumeration): Use iterative linear method.
745
+ update_data (dict, optional):
746
+ None: Calculate state estimation once with the current model attributes.
747
+ Or a dictionary for batch calculation with batch update.
748
+
749
+ - key: Component type name to be updated in batch.
750
+ - value:
751
+
752
+ - For homogeneous update batch (a 2D numpy structured array):
753
+
754
+ - Dimension 0: Each batch.
755
+ - Dimension 1: Each updated element per batch for this component type.
756
+ - For inhomogeneous update batch (a dictionary containing two keys):
757
+
758
+ - indptr: A 1D numpy int64 array with length n_batch + 1. Given batch number k, the
759
+ update array for this batch is data[indptr[k]:indptr[k + 1]]. This is the concept of
760
+ compressed sparse structure.
761
+ https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.csr_matrix.html
762
+ - data: 1D numpy structured array in flat.
763
+ threading (int, optional): Applicable only for batch calculation.
764
+
765
+ - < 0: Sequential
766
+ - = 0: Parallel, use number of hardware threads
767
+ - > 0: Specify number of parallel threads
768
+ output_component_types (ComponentAttributeMapping):
769
+
770
+ - None: Row based data for all component types.
771
+ - set[ComponentTypeVar] or list[ComponentTypeVar]: Row based data for the specified component types.
772
+ - ComponentAttributeFilterOptions: Columnar data for all component types.
773
+ - ComponentAttributeMappingDict:
774
+ key: ComponentType
775
+ value:
776
+ - None: Row based data for the specified component types.
777
+ - ComponentAttributeFilterOptions: Columnar data for the specified component types.
778
+ - set[str] | list[str]: Columnar data for the specified component types and attributes.
779
+ continue_on_batch_error (bool, optional):
780
+ Continue the program (instead of throwing error) if some scenarios fail.
781
+ You can still retrieve the errors and succeeded/failed scenarios via the batch_error.
782
+ decode_error (bool, optional):
783
+ Decode error messages to their derived types if possible.
784
+
785
+ Returns:
786
+ Dictionary of results of all components.
787
+
788
+ - key: Component type name to be updated in batch.
789
+ - value:
790
+
791
+ - For single calculation: 1D numpy structured array for the results of this component type.
792
+ - For batch calculation: 2D numpy structured array for the results of this component type.
793
+
794
+ - Dimension 0: Each batch.
795
+ - Dimension 1: The result of each element for this component type.
796
+
797
+ Raises:
798
+ Exception: In case an error in the core occurs, an exception will be thrown.
799
+ """
800
+ return self._calculate_state_estimation(
801
+ symmetric=symmetric,
802
+ error_tolerance=error_tolerance,
803
+ max_iterations=max_iterations,
804
+ calculation_method=calculation_method,
805
+ update_data=(_map_to_component_types(update_data) if update_data is not None else None),
806
+ threading=threading,
807
+ output_component_types=output_component_types,
808
+ continue_on_batch_error=continue_on_batch_error,
809
+ decode_error=decode_error,
810
+ )
811
+
812
+ @overload
813
+ def calculate_short_circuit(
814
+ self,
815
+ *,
816
+ calculation_method: CalculationMethod | str = ...,
817
+ update_data: None = ...,
818
+ threading: int = ...,
819
+ output_component_types: None | set[ComponentTypeVar] | list[ComponentTypeVar] = ...,
820
+ continue_on_batch_error: bool = ...,
821
+ decode_error: bool = ...,
822
+ short_circuit_voltage_scaling: ShortCircuitVoltageScaling | str,
823
+ ) -> SingleRowBasedDataset: ...
824
+ @overload
825
+ def calculate_short_circuit(
826
+ self,
827
+ *,
828
+ calculation_method: CalculationMethod | str = ...,
829
+ update_data: None = ...,
830
+ threading: int = ...,
831
+ output_component_types: ComponentAttributeFilterOptions = ...,
832
+ continue_on_batch_error: bool = ...,
833
+ decode_error: bool = ...,
834
+ short_circuit_voltage_scaling: ShortCircuitVoltageScaling | str,
835
+ ) -> SingleColumnarOutputDataset: ...
836
+ @overload
837
+ def calculate_short_circuit(
838
+ self,
839
+ *,
840
+ calculation_method: CalculationMethod | str = ...,
841
+ update_data: None = ...,
842
+ threading: int = ...,
843
+ output_component_types: ComponentAttributeMappingDict = ...,
844
+ continue_on_batch_error: bool = ...,
845
+ decode_error: bool = ...,
846
+ short_circuit_voltage_scaling: ShortCircuitVoltageScaling | str,
847
+ ) -> SingleOutputDataset: ...
848
+ @overload
849
+ def calculate_short_circuit(
850
+ self,
851
+ *,
852
+ calculation_method: CalculationMethod | str = ...,
853
+ update_data: BatchDataset = ...,
854
+ threading: int = ...,
855
+ output_component_types: None | set[ComponentTypeVar] | list[ComponentTypeVar] = ...,
856
+ continue_on_batch_error: bool = ...,
857
+ decode_error: bool = ...,
858
+ short_circuit_voltage_scaling: ShortCircuitVoltageScaling | str,
859
+ ) -> DenseBatchRowBasedOutputDataset: ...
860
+ @overload
861
+ def calculate_short_circuit(
862
+ self,
863
+ *,
864
+ calculation_method: CalculationMethod | str = ...,
865
+ update_data: BatchDataset = ...,
866
+ threading: int = ...,
867
+ output_component_types: ComponentAttributeFilterOptions = ...,
868
+ continue_on_batch_error: bool = ...,
869
+ decode_error: bool = ...,
870
+ short_circuit_voltage_scaling: ShortCircuitVoltageScaling | str,
871
+ ) -> DenseBatchColumnarOutputDataset: ...
872
+ @overload
873
+ def calculate_short_circuit(
874
+ self,
875
+ *,
876
+ calculation_method: CalculationMethod | str = ...,
877
+ update_data: BatchDataset = ...,
878
+ threading: int = ...,
879
+ output_component_types: ComponentAttributeMappingDict = ...,
880
+ continue_on_batch_error: bool = ...,
881
+ decode_error: bool = ...,
882
+ short_circuit_voltage_scaling: ShortCircuitVoltageScaling | str,
883
+ ) -> DenseBatchOutputDataset: ...
884
+ def calculate_short_circuit( # noqa: PLR0913
885
+ self,
886
+ *,
887
+ calculation_method: CalculationMethod | str = CalculationMethod.iec60909,
888
+ update_data: BatchDataset | None = None,
889
+ threading: int = -1,
890
+ output_component_types: ComponentAttributeMapping = None,
891
+ continue_on_batch_error: bool = False,
892
+ decode_error: bool = True,
893
+ short_circuit_voltage_scaling: ShortCircuitVoltageScaling | str = ShortCircuitVoltageScaling.maximum,
894
+ ) -> Dataset:
895
+ """
896
+ Calculate a short circuit once with the current model attributes.
897
+ Or calculate in batch with the given update dataset in batch
898
+
899
+ Args:
900
+ calculation_method (an enumeration): Use the iec60909 standard.
901
+ update_data:
902
+ None: calculate a short circuit once with the current model attributes.
903
+ Or a dictionary for batch calculation with batch update
904
+
905
+ - key: Component type name to be updated in batch
906
+ - value:
907
+
908
+ - For homogeneous update batch (a 2D numpy structured array):
909
+
910
+ - Dimension 0: each batch
911
+ - Dimension 1: each updated element per batch for this component type
912
+ - For inhomogeneous update batch (a dictionary containing two keys):
913
+
914
+ - indptr: A 1D numpy int64 array with length n_batch + 1. Given batch number k, the
915
+ update array for this batch is data[indptr[k]:indptr[k + 1]]. This is the concept of
916
+ compressed sparse structure.
917
+ https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.csr_matrix.html
918
+ - data: 1D numpy structured array in flat.
919
+ threading (int, optional): Applicable only for batch calculation.
920
+
921
+ - < 0: Sequential
922
+ - = 0: Parallel, use number of hardware threads
923
+ - > 0: Specify number of parallel threads
924
+ output_component_types (ComponentAttributeMapping):
925
+
926
+ - None: Row based data for all component types.
927
+ - set[ComponentTypeVar] or list[ComponentTypeVar]: Row based data for the specified component types.
928
+ - ComponentAttributeFilterOptions: Columnar data for all component types.
929
+ - ComponentAttributeMappingDict:
930
+ key: ComponentType
931
+ value:
932
+ - None: Row based data for the specified component types.
933
+ - ComponentAttributeFilterOptions: Columnar data for the specified component types.
934
+ - set[str] | list[str]: Columnar data for the specified component types and attributes.
935
+ continue_on_batch_error (bool, optional):
936
+ Continue the program (instead of throwing error) if some scenarios fail.
937
+ You can still retrieve the errors and succeeded/failed scenarios via the batch_error.
938
+ decode_error (bool, optional):
939
+ Decode error messages to their derived types if possible.
940
+ short_circuit_voltage_scaling ({ShortCircuitVoltageSaling, str}, optional):
941
+ Whether to use the maximum or minimum voltage scaling.
942
+ By default, the maximum voltage scaling is used to calculate the short circuit.
943
+
944
+ Returns:
945
+ Dictionary of results of all components.
946
+
947
+ - key: Component type name to be updated in batch.
948
+ - value:
949
+
950
+ - For single calculation: 1D numpy structured array for the results of this component type.
951
+ - For batch calculation: 2D numpy structured array for the results of this component type.
952
+
953
+ - Dimension 0: Each batch.
954
+ - Dimension 1: The result of each element for this component type.
955
+ Raises:
956
+ Exception: In case an error in the core occurs, an exception will be thrown.
957
+ """
958
+ return self._calculate_short_circuit(
959
+ calculation_method=calculation_method,
960
+ update_data=(_map_to_component_types(update_data) if update_data is not None else None),
961
+ threading=threading,
962
+ output_component_types=output_component_types,
963
+ continue_on_batch_error=continue_on_batch_error,
964
+ decode_error=decode_error,
965
+ short_circuit_voltage_scaling=short_circuit_voltage_scaling,
966
+ )
967
+
968
+ def __del__(self):
969
+ pgc.destroy_model(self._model_ptr)