ansys-systemcoupling-core 0.2.0__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ansys-systemcoupling-core might be problematic. Click here for more details.

Files changed (121) hide show
  1. ansys/systemcoupling/core/__init__.py +2 -4
  2. ansys/systemcoupling/core/adaptor/api_23_2/_add_participant.py +70 -0
  3. ansys/systemcoupling/core/adaptor/api_23_2/_solve.py +13 -0
  4. ansys/systemcoupling/core/adaptor/api_23_2/add_participant.py +38 -2
  5. ansys/systemcoupling/core/adaptor/api_23_2/case_root.py +13 -13
  6. ansys/systemcoupling/core/adaptor/api_23_2/setup_root.py +55 -49
  7. ansys/systemcoupling/core/adaptor/api_23_2/solution_root.py +42 -36
  8. ansys/systemcoupling/core/adaptor/api_23_2/solve.py +1 -1
  9. ansys/systemcoupling/core/adaptor/api_24_1/_add_participant.py +70 -0
  10. ansys/systemcoupling/core/adaptor/api_24_1/_solve.py +13 -0
  11. ansys/systemcoupling/core/adaptor/api_24_1/abort.py +39 -0
  12. ansys/systemcoupling/core/adaptor/api_24_1/activate_hidden.py +46 -0
  13. ansys/systemcoupling/core/adaptor/api_24_1/add_data_transfer.py +190 -0
  14. ansys/systemcoupling/core/adaptor/api_24_1/add_data_transfer_by_display_names.py +191 -0
  15. ansys/systemcoupling/core/adaptor/api_24_1/add_expression_function.py +61 -0
  16. ansys/systemcoupling/core/adaptor/api_24_1/add_interface.py +77 -0
  17. ansys/systemcoupling/core/adaptor/api_24_1/add_interface_by_display_names.py +78 -0
  18. ansys/systemcoupling/core/adaptor/api_24_1/add_named_expression.py +42 -0
  19. ansys/systemcoupling/core/adaptor/api_24_1/add_participant.py +140 -0
  20. ansys/systemcoupling/core/adaptor/api_24_1/add_reference_frame.py +40 -0
  21. ansys/systemcoupling/core/adaptor/api_24_1/add_transformation.py +102 -0
  22. ansys/systemcoupling/core/adaptor/api_24_1/analysis_control.py +249 -0
  23. ansys/systemcoupling/core/adaptor/api_24_1/apip.py +33 -0
  24. ansys/systemcoupling/core/adaptor/api_24_1/ascii_output.py +44 -0
  25. ansys/systemcoupling/core/adaptor/api_24_1/attribute.py +20 -0
  26. ansys/systemcoupling/core/adaptor/api_24_1/attribute_child.py +54 -0
  27. ansys/systemcoupling/core/adaptor/api_24_1/automatic_alignment_options.py +46 -0
  28. ansys/systemcoupling/core/adaptor/api_24_1/available_ports.py +40 -0
  29. ansys/systemcoupling/core/adaptor/api_24_1/avoid_data_reconstruction.py +46 -0
  30. ansys/systemcoupling/core/adaptor/api_24_1/case_root.py +62 -0
  31. ansys/systemcoupling/core/adaptor/api_24_1/clear_state.py +16 -0
  32. ansys/systemcoupling/core/adaptor/api_24_1/coupling_interface.py +20 -0
  33. ansys/systemcoupling/core/adaptor/api_24_1/coupling_interface_child.py +42 -0
  34. ansys/systemcoupling/core/adaptor/api_24_1/coupling_participant.py +23 -0
  35. ansys/systemcoupling/core/adaptor/api_24_1/coupling_participant_child.py +230 -0
  36. ansys/systemcoupling/core/adaptor/api_24_1/create_restart_point.py +29 -0
  37. ansys/systemcoupling/core/adaptor/api_24_1/data_transfer.py +20 -0
  38. ansys/systemcoupling/core/adaptor/api_24_1/data_transfer_child.py +187 -0
  39. ansys/systemcoupling/core/adaptor/api_24_1/delete_snapshot.py +28 -0
  40. ansys/systemcoupling/core/adaptor/api_24_1/delete_transformation.py +42 -0
  41. ansys/systemcoupling/core/adaptor/api_24_1/dimensionality.py +96 -0
  42. ansys/systemcoupling/core/adaptor/api_24_1/execution_control.py +186 -0
  43. ansys/systemcoupling/core/adaptor/api_24_1/expression.py +20 -0
  44. ansys/systemcoupling/core/adaptor/api_24_1/expression_child.py +36 -0
  45. ansys/systemcoupling/core/adaptor/api_24_1/expression_function.py +20 -0
  46. ansys/systemcoupling/core/adaptor/api_24_1/expression_function_child.py +46 -0
  47. ansys/systemcoupling/core/adaptor/api_24_1/external_data_file.py +24 -0
  48. ansys/systemcoupling/core/adaptor/api_24_1/fluent_input.py +67 -0
  49. ansys/systemcoupling/core/adaptor/api_24_1/fmu_parameter.py +20 -0
  50. ansys/systemcoupling/core/adaptor/api_24_1/fmu_parameter_child.py +156 -0
  51. ansys/systemcoupling/core/adaptor/api_24_1/generate_input_file.py +41 -0
  52. ansys/systemcoupling/core/adaptor/api_24_1/get_execution_command.py +30 -0
  53. ansys/systemcoupling/core/adaptor/api_24_1/get_machines.py +13 -0
  54. ansys/systemcoupling/core/adaptor/api_24_1/get_region_names_for_participant.py +31 -0
  55. ansys/systemcoupling/core/adaptor/api_24_1/get_setup_summary.py +25 -0
  56. ansys/systemcoupling/core/adaptor/api_24_1/get_snapshots.py +14 -0
  57. ansys/systemcoupling/core/adaptor/api_24_1/get_status_messages.py +52 -0
  58. ansys/systemcoupling/core/adaptor/api_24_1/get_transformation.py +43 -0
  59. ansys/systemcoupling/core/adaptor/api_24_1/global_stabilization.py +143 -0
  60. ansys/systemcoupling/core/adaptor/api_24_1/has_input_file_changed.py +36 -0
  61. ansys/systemcoupling/core/adaptor/api_24_1/import_system_coupling_input_file.py +36 -0
  62. ansys/systemcoupling/core/adaptor/api_24_1/initialize.py +27 -0
  63. ansys/systemcoupling/core/adaptor/api_24_1/instancing.py +23 -0
  64. ansys/systemcoupling/core/adaptor/api_24_1/instancing_child.py +62 -0
  65. ansys/systemcoupling/core/adaptor/api_24_1/interrupt.py +39 -0
  66. ansys/systemcoupling/core/adaptor/api_24_1/library.py +37 -0
  67. ansys/systemcoupling/core/adaptor/api_24_1/live_visualization.py +20 -0
  68. ansys/systemcoupling/core/adaptor/api_24_1/live_visualization_child.py +72 -0
  69. ansys/systemcoupling/core/adaptor/api_24_1/mapping_control.py +229 -0
  70. ansys/systemcoupling/core/adaptor/api_24_1/open.py +102 -0
  71. ansys/systemcoupling/core/adaptor/api_24_1/open_results_in_en_sight.py +56 -0
  72. ansys/systemcoupling/core/adaptor/api_24_1/open_snapshot.py +37 -0
  73. ansys/systemcoupling/core/adaptor/api_24_1/output_control.py +134 -0
  74. ansys/systemcoupling/core/adaptor/api_24_1/parameter.py +20 -0
  75. ansys/systemcoupling/core/adaptor/api_24_1/parameter_child.py +64 -0
  76. ansys/systemcoupling/core/adaptor/api_24_1/partition_participants.py +138 -0
  77. ansys/systemcoupling/core/adaptor/api_24_1/reference_frame.py +20 -0
  78. ansys/systemcoupling/core/adaptor/api_24_1/reference_frame_child.py +71 -0
  79. ansys/systemcoupling/core/adaptor/api_24_1/region.py +20 -0
  80. ansys/systemcoupling/core/adaptor/api_24_1/region_child.py +72 -0
  81. ansys/systemcoupling/core/adaptor/api_24_1/reload_expression_function_modules.py +14 -0
  82. ansys/systemcoupling/core/adaptor/api_24_1/results.py +89 -0
  83. ansys/systemcoupling/core/adaptor/api_24_1/save.py +51 -0
  84. ansys/systemcoupling/core/adaptor/api_24_1/save_snapshot.py +54 -0
  85. ansys/systemcoupling/core/adaptor/api_24_1/setup_root.py +195 -0
  86. ansys/systemcoupling/core/adaptor/api_24_1/shutdown.py +25 -0
  87. ansys/systemcoupling/core/adaptor/api_24_1/side.py +20 -0
  88. ansys/systemcoupling/core/adaptor/api_24_1/side_child.py +56 -0
  89. ansys/systemcoupling/core/adaptor/api_24_1/solution_control.py +103 -0
  90. ansys/systemcoupling/core/adaptor/api_24_1/solution_root.py +110 -0
  91. ansys/systemcoupling/core/adaptor/api_24_1/solve.py +30 -0
  92. ansys/systemcoupling/core/adaptor/api_24_1/stabilization.py +157 -0
  93. ansys/systemcoupling/core/adaptor/api_24_1/start_participants.py +47 -0
  94. ansys/systemcoupling/core/adaptor/api_24_1/step.py +57 -0
  95. ansys/systemcoupling/core/adaptor/api_24_1/transformation.py +21 -0
  96. ansys/systemcoupling/core/adaptor/api_24_1/transformation_child.py +62 -0
  97. ansys/systemcoupling/core/adaptor/api_24_1/type.py +38 -0
  98. ansys/systemcoupling/core/adaptor/api_24_1/unmapped_value_options.py +158 -0
  99. ansys/systemcoupling/core/adaptor/api_24_1/update_control.py +44 -0
  100. ansys/systemcoupling/core/adaptor/api_24_1/update_participant.py +61 -0
  101. ansys/systemcoupling/core/adaptor/api_24_1/variable.py +20 -0
  102. ansys/systemcoupling/core/adaptor/api_24_1/variable_child.py +232 -0
  103. ansys/systemcoupling/core/adaptor/api_24_1/write_csv_chart_files.py +21 -0
  104. ansys/systemcoupling/core/adaptor/api_24_1/write_ensight.py +46 -0
  105. ansys/systemcoupling/core/adaptor/impl/get_syc_version.py +35 -0
  106. ansys/systemcoupling/core/adaptor/impl/injected_commands.py +97 -5
  107. ansys/systemcoupling/core/adaptor/impl/root_source.py +2 -0
  108. ansys/systemcoupling/core/adaptor/impl/static_info.py +69 -41
  109. ansys/systemcoupling/core/adaptor/impl/syc_proxy.py +1 -1
  110. ansys/systemcoupling/core/adaptor/impl/types.py +12 -0
  111. ansys/systemcoupling/core/client/syc_container.py +2 -0
  112. ansys/systemcoupling/core/participant/manager.py +198 -0
  113. ansys/systemcoupling/core/participant/protocol.py +51 -0
  114. ansys/systemcoupling/core/session.py +7 -1
  115. ansys/systemcoupling/core/syc_version.py +26 -2
  116. {ansys_systemcoupling_core-0.2.0.dist-info → ansys_systemcoupling_core-0.3.0.dist-info}/METADATA +9 -9
  117. ansys_systemcoupling_core-0.3.0.dist-info/RECORD +230 -0
  118. ansys_systemcoupling_core-0.2.0.dist-info/RECORD +0 -129
  119. /ansys/systemcoupling/core/adaptor/{api_23_2 → api_24_1}/open_results_in_ensight.py +0 -0
  120. {ansys_systemcoupling_core-0.2.0.dist-info → ansys_systemcoupling_core-0.3.0.dist-info}/LICENSE +0 -0
  121. {ansys_systemcoupling_core-0.2.0.dist-info → ansys_systemcoupling_core-0.3.0.dist-info}/WHEEL +0 -0
@@ -1,10 +1,9 @@
1
1
  from copy import deepcopy
2
- from typing import Tuple
2
+ from typing import Dict, List, Tuple
3
3
 
4
4
  from ansys.systemcoupling.core.adaptor.impl.injected_commands import (
5
5
  get_injected_cmd_data,
6
6
  )
7
- from ansys.systemcoupling.core.syc_version import SYC_VERSION_DOT
8
7
  from ansys.systemcoupling.core.util.name_util import to_python_name
9
8
 
10
9
 
@@ -289,22 +288,85 @@ def get_extended_cmd_metadata(api) -> list:
289
288
  Object providing access to the System Coupling *native API*.
290
289
  """
291
290
 
291
+ def find_item_by_name(items: List[Dict], name: str) -> dict:
292
+ for item in items:
293
+ if item["name"] == name:
294
+ return item
295
+ raise RuntimeError(f"Did not find dict element of list with 'name' == {name}")
296
+
292
297
  def merge_data(target: list, source: list) -> None:
293
298
  target_names = set(d["name"] for d in target)
294
299
  source_names = set(d["name"] for d in source)
295
300
  new_names = source_names - target_names
296
301
  common_names = source_names & target_names
297
302
 
303
+ # Injected commands can either be completely new commands injected into
304
+ # the set of available commands, or can be "overrides" that replace the
305
+ # SyC command that would have been generated. Occasionally, we still
306
+ # want to generate the command that is being replaced so that we can
307
+ # call it internally. This is identified by a "pysyc_internal_name"
308
+ # field. In this case we split the command data into an injected command
309
+ # and a "normal" command but with the internal name as the pysyc name
310
+ # for the latter.
311
+
312
+ special_injected_commands = [
313
+ name
314
+ for name in common_names
315
+ if "pysyc_internal_name" in find_item_by_name(source, name)
316
+ ]
317
+
318
+ for name in special_injected_commands:
319
+ src_item = find_item_by_name(source, name)
320
+ tgt_item = find_item_by_name(target, name)
321
+
322
+ # Target item becomes a "normally" exposed SyC command except that
323
+ # it is "internal" on the PySyC side - only intended for internal
324
+ # PySyC use. It will be given a special internal name rather than
325
+ # the one derived from its SyC name. It will also have its doc
326
+ # info removed.
327
+
328
+ # - Take a copy of tgt before we remove doc.
329
+ tgt_copy = deepcopy(tgt_item)
330
+ if "doc" in tgt_item:
331
+ tgt_item["doc"] = "For internal use only."
332
+
333
+ for arg in tgt_item.get("args", []):
334
+ _, arg_info = arg
335
+ if "doc" in arg_info:
336
+ arg_info["doc"] = "..."
337
+
338
+ # - Name for PySyC exposure is the "pysyc_internal_name" from the source item
339
+ tgt_item["pyname"] = src_item["pysyc_internal_name"]
340
+
341
+ # Source item becomes a normal injected command. It is processed like the
342
+ # items in the common_names later but has special treatment for its
343
+ # doc and arguments. That is done here, so it will be removed from
344
+ # common_names and added to new_names.
345
+ for k, v in tgt_copy.items():
346
+ if k in ("essentialArgNames", "optionalArgNames", "args"):
347
+ src_item[k] = src_item.get(f"{k}_extra", []) + v
348
+ elif k == "doc":
349
+ prefix = src_item.get(f"{k}_prefix", "")
350
+ if prefix:
351
+ prefix += "\n\n"
352
+ suffix = src_item.get(f"{k}_suffix", "")
353
+ if suffix:
354
+ suffix = "\n" + suffix
355
+ src_item[k] = prefix + v + suffix
356
+ else:
357
+ src_item[k] = v
358
+
359
+ src_item["name"] = src_item["pyname"]
360
+
361
+ common_names.remove(name)
362
+ new_names.add(src_item["name"])
363
+
298
364
  for src_item in source:
299
365
  name = src_item["name"]
300
366
  if name in new_names:
301
367
  target.append(src_item)
302
368
  elif name in common_names:
303
- tgt_item = None
304
- for titem in target:
305
- if titem["name"] == name:
306
- tgt_item = titem
307
- break
369
+ tgt_item = find_item_by_name(target, name)
308
370
 
309
371
  # Single-level merge of source item dictionary into
310
372
  # target item dictionary. If any aspect of the arguments
@@ -317,37 +379,3 @@ def get_extended_cmd_metadata(api) -> list:
317
379
  injected_data = get_injected_cmd_data()
318
380
  merge_data(cmd_metadata, injected_data)
319
381
  return cmd_metadata
320
-
321
-
322
- def get_syc_version(api) -> str:
323
- """Get the System Coupling version.
324
-
325
- The version is returned in a string like ``"23.2"``.
326
-
327
- System Coupling versions earlier than 23.2 (2023 R2) do not expose
328
- the ``GetVersion`` query. Because the first version of the server
329
- that PySystemCoupling is able to connect to is 23.1 (2023 R1), the
330
- version is assumed to be 23.1 if no version query exists.
331
-
332
- Parameters
333
- ----------
334
- api : NativeApi
335
- Object providing access to the System Coupling *native API* .
336
- """
337
-
338
- def clean_version_string(version_in: str) -> str:
339
- year, _, release = version_in.partition(" ")
340
- if len(year) == 4 and year.startswith("20") and release.startswith("R"):
341
- try:
342
- year = int(year[2:])
343
- release = int(release[1:])
344
- return f"{year}.{release}"
345
- except:
346
- pass
347
- raise RuntimeError(
348
- f"Version string {version_in} has invalid format (expect '20yy Rn')."
349
- )
350
-
351
- cmds = api.GetCommandAndQueryMetadata()
352
- exists = any(cmd["name"] == "GetVersion" for cmd in cmds)
353
- return clean_version_string(api.GetVersion()) if exists else SYC_VERSION_DOT
@@ -1,7 +1,7 @@
1
+ from ansys.systemcoupling.core.adaptor.impl.get_syc_version import get_syc_version
1
2
  from ansys.systemcoupling.core.adaptor.impl.static_info import (
2
3
  get_dm_metadata,
3
4
  get_extended_cmd_metadata,
4
- get_syc_version,
5
5
  make_cmdonly_metadata,
6
6
  make_combined_metadata,
7
7
  )
@@ -29,6 +29,7 @@ import sys
29
29
  from typing import Dict, Generic, List, NewType, Tuple, TypeVar, Union
30
30
  import weakref
31
31
 
32
+ from ansys.systemcoupling.core.participant.protocol import ParticipantProtocol
32
33
  from ansys.systemcoupling.core.util import name_util
33
34
 
34
35
  # Type hints
@@ -246,6 +247,17 @@ class SettingsBase(Base, Generic[StateT]):
246
247
  out.flush()
247
248
 
248
249
 
250
+ # TODO: this doesn't make much sense as a "setting" but is needed to
251
+ # make the special form of add_participant work. Ideally we want to move
252
+ # away from treating command arguments as "settings" but that needs to be
253
+ # handled as a separate task.
254
+ class ParticipantSession(SettingsBase[ParticipantProtocol]):
255
+ """Object conforming to the ``ParticipantProtocol`` runtime protocol
256
+ for participant session objects."""
257
+
258
+ _state_type = ParticipantProtocol
259
+
260
+
249
261
  class Integer(SettingsBase[int]):
250
262
  """Provides an ``Integer`` object that represents an integer value setting."""
251
263
 
@@ -11,6 +11,8 @@ _DEFAULT_IMAGE_TAG = f"v{SYC_VERSION_DOT}.0"
11
11
 
12
12
 
13
13
  def _image_tag(version: str) -> str:
14
+ if version == "latest":
15
+ return version
14
16
  major, minor = normalize_version(version)
15
17
  return f"v{major}.{minor}.0"
16
18
 
@@ -0,0 +1,198 @@
1
+ import threading
2
+ from typing import Dict, List, Tuple
3
+
4
+ from ansys.systemcoupling.core.participant.protocol import ParticipantProtocol
5
+ from ansys.systemcoupling.core.util.logging import LOG
6
+
7
+
8
+ class ParticipantManager:
9
+ """Manages a System Coupling solution in which the participant solvers are
10
+ provided as "session" objects from other PyAnsys APIs.
11
+
12
+ These objects must conform (in a Python "duck typing" sense) to the
13
+ ``ParticipantProtocol`` protocol.
14
+
15
+ The ParticipantManager will play a role whenever participants are added to the
16
+ analysis using the ``add_participant`` command with ``participant_session`` being the one
17
+ and only argument.
18
+
19
+ In this case, the manager creates ``coupling_participant`` data model objects
20
+ based on queries to the session object. It will also store the session object
21
+ for later use if a solve is initiated.
22
+
23
+ If ``solve`` is called on the manager, it will coordinate both the connection of the
24
+ participants to System Coupling and, subsequently, the invocation of their solve
25
+ operations. In standard System Coupling terms, the solves that are initiated from
26
+ the "PyAnsys" environment will be regarded by the System Coupling solver as
27
+ "externally managed".
28
+
29
+ .. warning:
30
+ This facility should be regarded as sub-Beta level.
31
+ It is likely to be subject to further development, and has fairly limited utility
32
+ until more participant types support the protocol.
33
+ """
34
+
35
+ def __init__(self, syc_session):
36
+ self.__participants: Dict[str, ParticipantProtocol] = {}
37
+ self.__syc_session = syc_session
38
+ self.__connection_lock = threading.Lock()
39
+ self.clear()
40
+
41
+ def clear(self):
42
+ self.__participants: Dict[str, ParticipantProtocol] = {}
43
+ self.__n_connected = 0
44
+ self.__solve_exception = None
45
+
46
+ def add_participant(self, participant_session: ParticipantProtocol) -> str:
47
+ participant_name = (
48
+ f"{participant_session.participant_type}-{len(self.__participants) + 1}"
49
+ )
50
+
51
+ setup = self.__syc_session.setup
52
+
53
+ part_state = setup.coupling_participant.create(participant_name)
54
+ part_state.participant_type = participant_session.participant_type
55
+ part_state.participant_analysis_type = participant_session.get_analysis_type()
56
+ part_state.execution_control.option = "ExternallyManaged"
57
+
58
+ setup.analysis_control.analysis_type = (
59
+ participant_session.get_analysis_type()
60
+ ) # TODO: this logic isn't quite right, maybe delegate to controller
61
+
62
+ for variable in participant_session.get_variables():
63
+ part_state.variable.create(variable.name).set_state(
64
+ {
65
+ "tensor_type": variable.tensor_type,
66
+ "is_extensive": variable.is_extensive,
67
+ "location": variable.location,
68
+ "quantity_type": variable.quantity_type,
69
+ "participant_display_name": variable.display_name,
70
+ "display_name": variable.display_name.replace(
71
+ " ", "_"
72
+ ), # TODO: delegate this to controller
73
+ }
74
+ )
75
+
76
+ for region in participant_session.get_regions():
77
+ part_state.region.create(region.name).set_state(
78
+ {
79
+ "topology": region.topology,
80
+ "input_variables": region.input_variables,
81
+ "output_variables": region.output_variables,
82
+ "display_name": region.display_name,
83
+ }
84
+ )
85
+
86
+ self.__participants[participant_name] = participant_session
87
+ return participant_name
88
+
89
+ def solve(self):
90
+ self.__solve_exception = None
91
+ self._clear_n_connected()
92
+
93
+ if len(self.__participants) == 0:
94
+ # Fall back to normal solve
95
+ self.__syc_session.solution._solve()
96
+ return
97
+
98
+ # TODO : if we *don't* check for validation error before solve, and
99
+ # leave it for the SyC Solve() to find them, we see participants hang
100
+ # during connection. (This is independent of PySyC.)
101
+
102
+ if any(
103
+ msg
104
+ for msg in self.__syc_session.setup.get_status_messages()
105
+ if msg["level"] == "Error"
106
+ ):
107
+ raise RuntimeError(
108
+ "The setup data contains errors. solve() cannot proceed until these are fixed."
109
+ )
110
+
111
+ syc_solve_thread = threading.Thread(target=self._syc_solve)
112
+ try:
113
+ self._do_solve(syc_solve_thread)
114
+ finally:
115
+ syc_solve_thread.join()
116
+ LOG.info("SyC solve joined.")
117
+
118
+ if self.__solve_exception:
119
+ raise self.__solve_exception
120
+
121
+ def _do_solve(self, syc_solve_thread):
122
+ connection_threads = [
123
+ threading.Thread(
124
+ target=lambda host_port, name=name, part=participant: self._participant_connect(
125
+ name, host_port, part
126
+ ),
127
+ args=(self._get_host_and_port(name), name, participant),
128
+ )
129
+ for name, participant in self.__participants.items()
130
+ ]
131
+
132
+ LOG.info("Starting SyC solve thread...")
133
+ syc_solve_thread.start()
134
+ LOG.info("Waiting for participants to connect.")
135
+ _start_threads(connection_threads)
136
+ _join_threads(connection_threads)
137
+ connection_threads.clear()
138
+ if self._get_n_connected() < len(self.__participants):
139
+ LOG.error("Some participants were unable to connect to System Coupling.")
140
+ self.__syc_session.solution.abort()
141
+ else:
142
+ LOG.info("Participants connected.")
143
+
144
+ LOG.info("Starting participant solve threads.")
145
+ partsolve_threads = [
146
+ threading.Thread(target=participant.solve)
147
+ for participant in self.__participants.values()
148
+ ]
149
+ _start_threads(partsolve_threads)
150
+
151
+ LOG.info("Waiting for all solve threads to join.")
152
+ _join_threads(partsolve_threads)
153
+ LOG.info("All participant solve threads joined.")
154
+
155
+ def _clear_n_connected(self) -> None:
156
+ with self.__connection_lock:
157
+ self.__n_connected = 0
158
+
159
+ def _get_n_connected(self) -> int:
160
+ with self.__connection_lock:
161
+ return self.__n_connected
162
+
163
+ def _increment_n_connected(self) -> None:
164
+ with self.__connection_lock:
165
+ self.__n_connected += 1
166
+
167
+ def _get_host_and_port(self, participant_name: str) -> Tuple[str, int]:
168
+ port, host = self.__syc_session._native_api.GetServerInfo()
169
+ return host, port
170
+
171
+ def _participant_connect(
172
+ self, name: str, host_port: Tuple[str, int], participant: ParticipantProtocol
173
+ ) -> None:
174
+ try:
175
+ participant.connect(*host_port, name)
176
+ self._increment_n_connected()
177
+ except Exception as e:
178
+ LOG.error(f"Participant {name} failed to connect. Exception: {e}")
179
+
180
+ def _syc_solve(self):
181
+ try:
182
+ # We use `syc_session.solution._solve` here as it is
183
+ # the lower level solve command. `sys_session.solution.solve`
184
+ # would bring us recursively back into *this* function
185
+ self.__syc_session.solution._solve()
186
+ except Exception as e:
187
+ self.__solve_exception = e
188
+ LOG.error(f"Solve terminated with exception: {e}.")
189
+
190
+
191
+ def _start_threads(threads: List[threading.Thread]) -> None:
192
+ for thread in threads:
193
+ thread.start()
194
+
195
+
196
+ def _join_threads(threads: List[threading.Thread]) -> None:
197
+ for thread in threads:
198
+ thread.join()
@@ -0,0 +1,51 @@
1
+ from dataclasses import dataclass
2
+ from typing import List, Protocol
3
+
4
+
5
+ @dataclass
6
+ class Variable(Protocol):
7
+ name: str
8
+ display_name: str
9
+ tensor_type: str
10
+ is_extensive: bool
11
+ location: str
12
+ quantity_type: str
13
+
14
+
15
+ @dataclass
16
+ class Region(Protocol):
17
+ name: str
18
+ display_name: str
19
+ topology: str
20
+ input_variables: List[str]
21
+ output_variables: List[str]
22
+
23
+
24
+ class ParticipantProtocol(Protocol):
25
+ """Protocol class to which PyAnsys sessions that are added to a PySystemCoupling
26
+ session must conform."""
27
+
28
+ @property
29
+ def participant_type(self) -> str:
30
+ """Type of participant."""
31
+ ...
32
+
33
+ def get_variables(self) -> List[Variable]:
34
+ """List of variables that can be transferred from the participant."""
35
+ ...
36
+
37
+ def get_regions(self) -> List[Region]:
38
+ """List of regions on which data transfers can occur."""
39
+ ...
40
+
41
+ def get_analysis_type(self) -> str:
42
+ """The type of the analysis - "Steady" or "Transient"."""
43
+ ...
44
+
45
+ def connect(self, host: str, port: int, name: str) -> None:
46
+ """Establish connection between the participant solver and System Coupling."""
47
+ ...
48
+
49
+ def solve(self) -> None:
50
+ """Run the participant's solve operation."""
51
+ ...
@@ -8,6 +8,7 @@ from ansys.systemcoupling.core.adaptor.impl.injected_commands import (
8
8
  from ansys.systemcoupling.core.adaptor.impl.root_source import get_root
9
9
  from ansys.systemcoupling.core.adaptor.impl.syc_proxy import SycProxy
10
10
  from ansys.systemcoupling.core.native_api import NativeApi
11
+ from ansys.systemcoupling.core.participant.manager import ParticipantManager
11
12
 
12
13
  if os.environ.get("PYSYC_DOC_BUILD_VERSION"):
13
14
  # It is useful to import explicit types while building doc as it
@@ -60,6 +61,7 @@ class Session:
60
61
  self.__rpc = rpc
61
62
  self.__native_api = None
62
63
  self.__syc_version = None
64
+ self.__part_mgr = ParticipantManager(self)
63
65
 
64
66
  def exit(self) -> None:
65
67
  """Close the System Coupling server instance.
@@ -152,7 +154,11 @@ class Session:
152
154
  version = sycproxy.get_version()
153
155
  self.__syc_version = version.replace(".", "_")
154
156
  root = get_root(sycproxy, category=category, version=self.__syc_version)
155
- sycproxy.set_injected_commands(get_injected_cmd_map(category, root, self.__rpc))
157
+ sycproxy.set_injected_commands(
158
+ get_injected_cmd_map(
159
+ self.__syc_version, category, root, self.__part_mgr, self.__rpc
160
+ )
161
+ )
156
162
  return (root, sycproxy)
157
163
 
158
164
  @property
@@ -2,8 +2,8 @@ from typing import Tuple
2
2
 
3
3
  # Define constants relating to the default/current version of System Coupling
4
4
 
5
- SYC_MAJOR_VERSION = 23
6
- SYC_MINOR_VERSION = 2
5
+ SYC_MAJOR_VERSION = 24
6
+ SYC_MINOR_VERSION = 1
7
7
 
8
8
  SYC_VERSION_CONCAT = f"{SYC_MAJOR_VERSION}{SYC_MINOR_VERSION}"
9
9
  SYC_VERSION_DOT = f"{SYC_MAJOR_VERSION}.{SYC_MINOR_VERSION}"
@@ -56,3 +56,27 @@ def normalize_version(version: str) -> Tuple[int, int]:
56
56
  return process_major_minor(version[0:2], version[2])
57
57
  else:
58
58
  raise_error()
59
+
60
+
61
+ def compare_versions(version_a: str, version_b: str) -> int:
62
+ """Compares two version strings that are in any form that can be parsed by
63
+ ``normalize_version``.
64
+
65
+ Returns < 0 if ``version_a`` is an earlier version than ``version_b``, 0
66
+ if they are the same version, and > 0 if ``version_a`` is a later version
67
+ than ``version_b``.
68
+
69
+ Parameters
70
+ ----------
71
+ version_a : str
72
+ The first version string to compare.
73
+ version_b: str
74
+ The second version string to compare.
75
+ """
76
+ va_maj, va_min = normalize_version(version_a)
77
+ vb_maj, vb_min = normalize_version(version_b)
78
+
79
+ if va_maj == vb_maj:
80
+ return va_min - vb_min
81
+ else:
82
+ return va_maj - vb_maj
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ansys-systemcoupling-core
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: A Python wrapper for Ansys System Coupling.
5
5
  Author-email: "ANSYS, Inc." <pyansys.support@ansys.com>
6
6
  Maintainer-email: PyAnsys developers <pyansys.maintainers@ansys.com>
@@ -19,7 +19,7 @@ Classifier: Programming Language :: Python :: 3.9
19
19
  Classifier: Programming Language :: Python :: 3.10
20
20
  Requires-Dist: ansys-api-systemcoupling==0.1.0
21
21
  Requires-Dist: grpcio>=1.30.0
22
- Requires-Dist: grpcio-status>=1.30.0,<1.56.1
22
+ Requires-Dist: grpcio-status>=1.30.0,<1.58.1
23
23
  Requires-Dist: googleapis-common-protos>=1.50.0
24
24
  Requires-Dist: protobuf>=3.20.1,<4.0.0
25
25
  Requires-Dist: psutil>=5.7.0
@@ -29,24 +29,24 @@ Requires-Dist: importlib-metadata>=4.0
29
29
  Requires-Dist: build ; extra == "build"
30
30
  Requires-Dist: black==23.7.0 ; extra == "classesgen"
31
31
  Requires-Dist: isort==5.12.0 ; extra == "classesgen"
32
- Requires-Dist: ansys-sphinx-theme==0.9.9 ; extra == "doc"
32
+ Requires-Dist: ansys-sphinx-theme==0.11.2 ; extra == "doc"
33
33
  Requires-Dist: jupyter_sphinx==0.4.0 ; extra == "doc"
34
34
  Requires-Dist: matplotlib ; extra == "doc"
35
35
  Requires-Dist: numpydoc==1.5.0 ; extra == "doc"
36
36
  Requires-Dist: pypandoc==1.11 ; extra == "doc"
37
37
  Requires-Dist: pytest-sphinx==0.5.0 ; extra == "doc"
38
- Requires-Dist: Sphinx==7.0.1 ; extra == "doc"
38
+ Requires-Dist: Sphinx==7.2.6 ; extra == "doc"
39
39
  Requires-Dist: sphinx-autobuild==2021.3.14 ; extra == "doc"
40
- Requires-Dist: sphinx-autodoc-typehints==1.23.3 ; extra == "doc"
40
+ Requires-Dist: sphinx-autodoc-typehints==1.24.0 ; extra == "doc"
41
41
  Requires-Dist: sphinx-copybutton==0.5.2 ; extra == "doc"
42
- Requires-Dist: sphinx-gallery==0.13.0 ; extra == "doc"
43
- Requires-Dist: sphinx-notfound-page==0.8.3 ; extra == "doc"
44
- Requires-Dist: sphinxcontrib-websupport==1.2.4 ; extra == "doc"
42
+ Requires-Dist: sphinx-gallery==0.14.0 ; extra == "doc"
43
+ Requires-Dist: sphinx-notfound-page==1.0.0 ; extra == "doc"
44
+ Requires-Dist: sphinxcontrib-websupport==1.2.6 ; extra == "doc"
45
45
  Requires-Dist: sphinxemoji==0.2.0 ; extra == "doc"
46
46
  Requires-Dist: ansys-fluent-core ; extra == "doc"
47
47
  Requires-Dist: ansys-dpf-core ; extra == "doc"
48
48
  Requires-Dist: codespell==2.2.5 ; extra == "style"
49
- Requires-Dist: flake8==6.0.0 ; extra == "style"
49
+ Requires-Dist: flake8==6.1.0 ; extra == "style"
50
50
  Requires-Dist: pytest ; extra == "tests"
51
51
  Requires-Dist: pytest-cov ; extra == "tests"
52
52
  Requires-Dist: psutil>=5.7.0 ; extra == "tests"