ansys-mechanical-core 0.10.10__py3-none-any.whl → 0.11.12__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.
Files changed (46) hide show
  1. ansys/mechanical/core/__init__.py +11 -4
  2. ansys/mechanical/core/_version.py +48 -47
  3. ansys/mechanical/core/embedding/__init__.py +1 -1
  4. ansys/mechanical/core/embedding/addins.py +1 -7
  5. ansys/mechanical/core/embedding/app.py +610 -281
  6. ansys/mechanical/core/embedding/app_libraries.py +24 -5
  7. ansys/mechanical/core/embedding/appdata.py +16 -4
  8. ansys/mechanical/core/embedding/background.py +106 -0
  9. ansys/mechanical/core/embedding/cleanup_gui.py +61 -0
  10. ansys/mechanical/core/embedding/enum_importer.py +2 -2
  11. ansys/mechanical/core/embedding/imports.py +27 -7
  12. ansys/mechanical/core/embedding/initializer.py +105 -53
  13. ansys/mechanical/core/embedding/loader.py +19 -9
  14. ansys/mechanical/core/embedding/logger/__init__.py +219 -216
  15. ansys/mechanical/core/embedding/logger/environ.py +1 -1
  16. ansys/mechanical/core/embedding/logger/linux_api.py +1 -1
  17. ansys/mechanical/core/embedding/logger/sinks.py +1 -1
  18. ansys/mechanical/core/embedding/logger/windows_api.py +2 -2
  19. ansys/mechanical/core/embedding/poster.py +38 -4
  20. ansys/mechanical/core/embedding/resolver.py +41 -44
  21. ansys/mechanical/core/embedding/runtime.py +1 -1
  22. ansys/mechanical/core/embedding/shims.py +9 -8
  23. ansys/mechanical/core/embedding/ui.py +228 -0
  24. ansys/mechanical/core/embedding/utils.py +1 -1
  25. ansys/mechanical/core/embedding/viz/__init__.py +1 -1
  26. ansys/mechanical/core/embedding/viz/{pyvista_plotter.py → embedding_plotter.py} +24 -8
  27. ansys/mechanical/core/embedding/viz/usd_converter.py +59 -25
  28. ansys/mechanical/core/embedding/viz/utils.py +32 -2
  29. ansys/mechanical/core/embedding/warnings.py +1 -1
  30. ansys/mechanical/core/errors.py +2 -1
  31. ansys/mechanical/core/examples/__init__.py +1 -1
  32. ansys/mechanical/core/examples/downloads.py +10 -5
  33. ansys/mechanical/core/feature_flags.py +51 -0
  34. ansys/mechanical/core/ide_config.py +212 -0
  35. ansys/mechanical/core/launcher.py +9 -9
  36. ansys/mechanical/core/logging.py +14 -2
  37. ansys/mechanical/core/mechanical.py +2324 -2237
  38. ansys/mechanical/core/misc.py +176 -176
  39. ansys/mechanical/core/pool.py +712 -712
  40. ansys/mechanical/core/run.py +321 -246
  41. {ansys_mechanical_core-0.10.10.dist-info → ansys_mechanical_core-0.11.12.dist-info}/LICENSE +7 -7
  42. {ansys_mechanical_core-0.10.10.dist-info → ansys_mechanical_core-0.11.12.dist-info}/METADATA +57 -56
  43. ansys_mechanical_core-0.11.12.dist-info/RECORD +45 -0
  44. {ansys_mechanical_core-0.10.10.dist-info → ansys_mechanical_core-0.11.12.dist-info}/WHEEL +1 -1
  45. {ansys_mechanical_core-0.10.10.dist-info → ansys_mechanical_core-0.11.12.dist-info}/entry_points.txt +1 -0
  46. ansys_mechanical_core-0.10.10.dist-info/RECORD +0 -40
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates.
1
+ # Copyright (C) 2022 - 2025 ANSYS, Inc. and/or its affiliates.
2
2
  # SPDX-License-Identifier: MIT
3
3
  #
4
4
  #
@@ -25,16 +25,17 @@
25
25
  These shims are used when APIs are released in newer versions of Mechanical,
26
26
  but workarounds exist in an older release
27
27
  """
28
+ import warnings
28
29
 
29
30
 
30
31
  def import_materials(
31
32
  app: "ansys.mechanical.core.embedding.Application", material_file: str
32
33
  ) -> None:
33
34
  """Import material from matml file."""
34
- if app._version >= 232:
35
- materials = app.DataModel.Project.Model.Materials
36
- materials.Import(material_file)
37
- else: # pragma: no cover
38
- material_file = material_file.replace("\\", "\\\\")
39
- script = 'DS.Tree.Projects.Item(1).LoadEngrDataLibraryFromFile("' + material_file + '");'
40
- app.ExtAPI.Application.ScriptByName("jscript").ExecuteCommand(script)
35
+ warnings.warn(
36
+ "Use of this function is deprecated. Use Model.Materials.Import() directly.",
37
+ DeprecationWarning,
38
+ stacklevel=2,
39
+ )
40
+ materials = app.DataModel.Project.Model.Materials
41
+ materials.Import(material_file)
@@ -0,0 +1,228 @@
1
+ # Copyright (C) 2022 - 2025 ANSYS, Inc. and/or its affiliates.
2
+ # SPDX-License-Identifier: MIT
3
+ #
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+ """Run Mechanical UI from Python."""
23
+
24
+ from pathlib import Path
25
+
26
+ # Subprocess is needed to launch the GUI and clean up the process on close.
27
+ # Excluding bandit check.
28
+ from subprocess import Popen # nosec: B404
29
+ import sys
30
+ import tempfile
31
+ import typing
32
+
33
+
34
+ class UILauncher:
35
+ """Launch the GUI using a temporary mechdb file."""
36
+
37
+ def __init__(self, dry_run: bool = False):
38
+ """Initialize UILauncher class."""
39
+ self._dry_run = dry_run
40
+
41
+ def save_original(self, app: "ansys.mechanical.core.embedding.App") -> None:
42
+ """Save the active mechdb file.
43
+
44
+ Parameters
45
+ ----------
46
+ app: ansys.mechanical.core.embedding.app.App
47
+ A Mechanical embedding application.
48
+ """
49
+ app.save()
50
+
51
+ def save_temp_copy(
52
+ self, app: "ansys.mechanical.core.embedding.App"
53
+ ) -> typing.Union[Path, Path]:
54
+ """Save a new mechdb file with a temporary name.
55
+
56
+ Parameters
57
+ ----------
58
+ app: ansys.mechanical.core.embedding.app.App
59
+ A Mechanical embedding application.
60
+ """
61
+ # Identify the mechdb of the saved session from save_original()
62
+ project_directory = Path(app.project_directory)
63
+ project_directory_parent = project_directory.parent
64
+ mechdb_file = (
65
+ project_directory_parent / f"{project_directory.parts[-1].split('_')[0]}.mechdb"
66
+ )
67
+
68
+ # Get name of NamedTemporaryFile
69
+ temp_file_name = tempfile.NamedTemporaryFile(
70
+ dir=project_directory_parent, suffix=".mechdb", delete=True
71
+ ).name
72
+
73
+ # Save app with name of temporary file
74
+ app.save_as(temp_file_name)
75
+
76
+ return mechdb_file, temp_file_name
77
+
78
+ def open_original(self, app: "ansys.mechanical.core.embedding.App", mechdb_file: str) -> None:
79
+ """Open the original mechdb file from save_original().
80
+
81
+ Parameters
82
+ ----------
83
+ app: ansys.mechanical.core.embedding.app.App
84
+ A Mechanical embedding application.
85
+ mechdb_file: str
86
+ The full path to the active mechdb file.
87
+ """
88
+ app.open(mechdb_file)
89
+
90
+ def graphically_launch_temp(
91
+ self, app: "ansys.mechanical.core.embedding.App", temp_file: Path
92
+ ) -> typing.Union[Popen, str]:
93
+ """Launch the GUI for the mechdb file with a temporary name from save_temp_copy().
94
+
95
+ Parameters
96
+ ----------
97
+ app: ansys.mechanical.core.embedding.app.App
98
+ A Mechanical embedding application.
99
+ temp_file: pathlib.Path
100
+ The full path to the temporary mechdb file.
101
+
102
+ Returns
103
+ -------
104
+ subprocess.Popen
105
+ The subprocess that launches the GUI for the temporary mechdb file.
106
+ """
107
+ # The ansys-mechanical command to launch the GUI in a subprocess
108
+ args = [
109
+ "ansys-mechanical",
110
+ "--project-file",
111
+ temp_file,
112
+ "--graphical",
113
+ "--revision",
114
+ str(app.version),
115
+ ]
116
+ if not self._dry_run:
117
+ # The subprocess that uses ansys-mechanical to launch the GUI of the temporary
118
+ # mechdb file
119
+ process = Popen(args) # nosec: B603 # pragma: no cover
120
+ return process
121
+ else:
122
+ # Return a string containing the args
123
+ return " ".join(args)
124
+
125
+ def _cleanup_gui(self, process: Popen, temp_mechdb_path: Path) -> None:
126
+ """Remove the temporary mechdb file and folder when the GUI is closed.
127
+
128
+ Parameters
129
+ ----------
130
+ process: subprocess.Popen
131
+ The subprocess that launched the GUI of the temporary mechdb file.
132
+ temp_mechdb_path: pathlib.Path
133
+ The full path to the temporary mechdb file.
134
+ """
135
+ # Get the path to the cleanup script
136
+ cleanup_script = Path(__file__).parent / "cleanup_gui.py" # pragma: no cover
137
+
138
+ if not self._dry_run:
139
+ # Open a subprocess to remove the temporary mechdb file and folder when the process ends
140
+ Popen(
141
+ [sys.executable, cleanup_script, str(process.pid), temp_mechdb_path]
142
+ ) # pragma: no cover # nosec: B603
143
+
144
+
145
+ def _is_saved(app: "ansys.mechanical.core.embedding.App") -> bool:
146
+ """Check if the mechdb file has been saved and raise an exception if not.
147
+
148
+ Parameters
149
+ ----------
150
+ app: ansys.mechanical.core.embedding.app.App
151
+ A Mechanical embedding application.
152
+
153
+ Returns
154
+ -------
155
+ bool
156
+ ``True`` when the embedded app has been saved.
157
+ ``False`` when the embedded app has not been saved.
158
+ """
159
+ try:
160
+ app.save()
161
+ except:
162
+ raise Exception("The App must have already been saved before using launch_ui!")
163
+ return True
164
+
165
+
166
+ def _launch_ui(
167
+ app: "ansys.mechanical.core.embedding.App", delete_tmp_on_close: bool, launcher: UILauncher
168
+ ) -> None:
169
+ """Launch the Mechanical UI if the mechdb file has been saved.
170
+
171
+ Parameters
172
+ ----------
173
+ app: ansys.mechanical.core.embedding.app.App
174
+ A Mechanical embedding application.
175
+ delete_tmp_on_close: bool
176
+ Whether to delete the temporary mechdb file when the GUI is closed.
177
+ By default, this is ``True``.
178
+ launcher: UILauncher
179
+ Launch the GUI using a temporary mechdb file.
180
+ """
181
+ if _is_saved(app):
182
+ # Save the active mechdb file.
183
+ launcher.save_original(app)
184
+ # Save a new mechdb file with a temporary name.
185
+ mechdb_file, temp_file = launcher.save_temp_copy(app)
186
+ # Open the original mechdb file from save_original().
187
+ launcher.open_original(app, str(mechdb_file))
188
+ # Launch the GUI for the mechdb file with a temporary name from save_temp_copy().
189
+ process = launcher.graphically_launch_temp(app, temp_file)
190
+
191
+ # If it's a dry run and graphically_launch_temp returned a string, print the string
192
+ if isinstance(process, str):
193
+ print(process)
194
+
195
+ # If the user wants the temporary file to be deleted and graphically_launch_temp
196
+ # returned a process. By default, this is True
197
+ if delete_tmp_on_close and not isinstance(process, str):
198
+ # Remove the temporary mechdb file and folder when the GUI is closed.
199
+ launcher._cleanup_gui(process, temp_file) # pragma: no cover
200
+ else:
201
+ # Let the user know that the mechdb started above will not automatically get cleaned up
202
+ print(
203
+ f"""Opened a new mechanical session based on {mechdb_file} named {temp_file}.
204
+ PyMechanical will not delete it after use."""
205
+ )
206
+
207
+
208
+ def launch_ui(
209
+ app: "ansys.mechanical.core.embedding.App",
210
+ delete_tmp_on_close: bool = True,
211
+ dry_run: bool = False,
212
+ ) -> None:
213
+ """Launch the Mechanical UI.
214
+
215
+ Precondition: Mechanical has to have already been saved
216
+ Side effect: If Mechanical has ever been saved, it overwrites that save.
217
+
218
+ Parameters
219
+ ----------
220
+ app: ansys.mechanical.core.embedding.app.App
221
+ A Mechanical embedding application.
222
+ delete_tmp_on_close: bool
223
+ Whether to delete the temporary mechdb file when the GUI is closed.
224
+ By default, this is ``True``.
225
+ dry_run: bool
226
+ Whether or not to launch the GUI. By default, this is ``False``.
227
+ """
228
+ _launch_ui(app, delete_tmp_on_close, UILauncher(dry_run))
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates.
1
+ # Copyright (C) 2022 - 2025 ANSYS, Inc. and/or its affiliates.
2
2
  # SPDX-License-Identifier: MIT
3
3
  #
4
4
  #
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates.
1
+ # Copyright (C) 2022 - 2025 ANSYS, Inc. and/or its affiliates.
2
2
  # SPDX-License-Identifier: MIT
3
3
  #
4
4
  #
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates.
1
+ # Copyright (C) 2022 - 2025 ANSYS, Inc. and/or its affiliates.
2
2
  # SPDX-License-Identifier: MIT
3
3
  #
4
4
  #
@@ -29,6 +29,7 @@ clr.AddReference("Ansys.ACT.Interfaces")
29
29
 
30
30
  import Ansys # isort: skip
31
31
 
32
+ from ansys.tools.visualization_interface import Plotter
32
33
  import numpy as np
33
34
  import pyvista as pv
34
35
 
@@ -44,7 +45,7 @@ def _transform_to_pyvista(transform: "Ansys.ACT.Math.Matrix4D"):
44
45
  return np_transform
45
46
 
46
47
 
47
- def _get_nodes_and_coords(tri_tessellation: "Ansys.Mechanical.Scenegraph.TriTessellationNode"):
48
+ def _get_tri_nodes_and_coords(tri_tessellation: "Ansys.Mechanical.Scenegraph.TriTessellationNode"):
48
49
  """Get the nodes and indices of the TriTessellationNode.
49
50
 
50
51
  pyvista format expects a number of vertices per facet which is always 3
@@ -55,18 +56,33 @@ def _get_nodes_and_coords(tri_tessellation: "Ansys.Mechanical.Scenegraph.TriTess
55
56
  return np_coordinates, np_indices
56
57
 
57
58
 
58
- def plot_model(app):
59
- """Plot the model."""
60
- plotter = pv.Plotter()
59
+ def _get_nodes_and_coords(node: "Ansys.Mechanical.Scenegraph.Node"):
60
+ """Get the nodes and indices of the Scenegraph node.
61
61
 
62
+ Currently only supported for tri tessellation nodes
63
+ """
64
+ if isinstance(node, Ansys.Mechanical.Scenegraph.TriTessellationNode):
65
+ return _get_tri_nodes_and_coords(node)
66
+
67
+ # TODO - support line tessellation node. See issue #809
68
+ # if isinstance(node, Ansys.Mechanical.Scenegraph.LineTessellationNode):
69
+ return None, None
70
+
71
+
72
+ def to_plotter(app: "ansys.mechanical.core.embedding.App"):
73
+ """Convert the app's geometry to an ``ansys.tools.visualization_interface.Plotter`` instance."""
74
+ plotter = Plotter()
75
+
76
+ # TODO - use get_scene from utils instead of looping over bodies directly here.
62
77
  for body in app.DataModel.GetObjectsByType(
63
78
  Ansys.Mechanical.DataModel.Enums.DataModelObjectCategory.Body
64
79
  ):
65
80
  scenegraph_node = Ansys.ACT.Mechanical.Tools.ScenegraphHelpers.GetScenegraph(body)
66
81
  np_coordinates, np_indices = _get_nodes_and_coords(scenegraph_node.Child)
82
+ if np_coordinates is None or np_indices is None:
83
+ continue
67
84
  pv_transform = _transform_to_pyvista(scenegraph_node.Transform)
68
85
  polydata = pv.PolyData(np_coordinates, np_indices).transform(pv_transform)
69
86
  color = pv.Color(bgr_to_rgb_tuple(body.Color))
70
- plotter.add_mesh(polydata, color=color, smooth_shading=True)
71
-
72
- plotter.show()
87
+ plotter.plot(polydata, color=color, smooth_shading=True)
88
+ return plotter
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates.
1
+ # Copyright (C) 2022 - 2025 ANSYS, Inc. and/or its affiliates.
2
2
  # SPDX-License-Identifier: MIT
3
3
  #
4
4
  #
@@ -24,15 +24,9 @@
24
24
 
25
25
  import typing
26
26
 
27
- import clr
28
27
  from pxr import Gf, Usd, UsdGeom
29
28
 
30
- clr.AddReference("Ansys.Mechanical.DataModel")
31
- clr.AddReference("Ansys.ACT.Interfaces")
32
-
33
- import Ansys # isort: skip
34
-
35
- from .utils import bgr_to_rgb_tuple, get_nodes_and_coords
29
+ from .utils import bgr_to_rgb_tuple, get_nodes_and_coords, get_scene
36
30
 
37
31
 
38
32
  def _transform_to_rotation_translation(
@@ -73,37 +67,77 @@ def _convert_tri_tessellation_node(
73
67
  return mesh_prim
74
68
 
75
69
 
76
- def _convert_transform_node(
77
- node: "Ansys.Mechanical.Scenegraph.TransformNode",
78
- stage: Usd.Stage,
79
- path: str,
80
- rgb: typing.Tuple[int, int, int],
70
+ def _create_prim_with_transform(
71
+ stage: Usd.Stage, path: str, node: "Ansys.Mechanical.Scenegraph.TransformNode"
81
72
  ) -> Usd.Prim:
82
- """Convert a mechanical transform node into a Usd Xform prim."""
73
+ """Create an empty Usd Xform prim based on a mechanical transform node."""
83
74
  prim = UsdGeom.Xform.Define(stage, path)
84
75
  rotation, translation = _transform_to_rotation_translation(node.Transform)
85
76
  prim.AddOrientOp().Set(rotation)
86
77
  prim.AddTranslateOp().Set(translation)
78
+ return prim
79
+
80
+
81
+ def _convert_transform_node(
82
+ node: "Ansys.Mechanical.Scenegraph.TransformNode",
83
+ stage: Usd.Stage,
84
+ path: str,
85
+ rgb: typing.Tuple[int, int, int],
86
+ ) -> None:
87
+ """Add a Usd prim to the stage based on the given mechanical transform node.
88
+
89
+ Currently only supports transforms that contain a single tri tessellation node.
90
+ """
91
+ import clr
92
+
93
+ clr.AddReference("Ansys.Mechanical.DataModel")
94
+ clr.AddReference("Ansys.ACT.Interfaces")
95
+
96
+ import Ansys # isort: skip
97
+
87
98
  child_node = node.Child
88
- child_path = prim.GetPath().AppendPath("TriTessellation")
89
99
  if isinstance(child_node, Ansys.Mechanical.Scenegraph.TriTessellationNode):
100
+ prim = _create_prim_with_transform(stage, path, node)
101
+ child_path = prim.GetPath().AppendPath("TriTessellation")
90
102
  _convert_tri_tessellation_node(child_node, stage, child_path, rgb)
91
- return prim
92
103
 
93
104
 
94
- def to_usd_stage(app: "ansys.mechanical.core.embedding.App", name: str) -> None:
95
- """Convert mechanical scene to usd stage."""
96
- stage = Usd.Stage.CreateNew(name)
105
+ def _convert_attribute_node(
106
+ node: "Ansys.Mechanical.Scenegraph.AttributeNode",
107
+ stage: Usd.Stage,
108
+ path: str,
109
+ ) -> None:
110
+ """Add a Usd Prim of the child node with the given attributes node.
111
+
112
+ Currently only supports color attributes.
113
+ """
114
+ import clr
97
115
 
116
+ clr.AddReference("Ansys.Mechanical.DataModel")
117
+ clr.AddReference("Ansys.ACT.Interfaces")
118
+
119
+ import Ansys # isort: skip
120
+
121
+ child_node = node.Child
122
+ color = node.Property(Ansys.Mechanical.Scenegraph.ScenegraphIntAttributes.Color)
123
+ _convert_transform_node(child_node, stage, path, bgr_to_rgb_tuple(color))
124
+
125
+
126
+ def load_into_usd_stage(scene: "Ansys.Mechanical.Scenegraph.GroupNode", stage: Usd.Stage) -> None:
127
+ """Load mechanical scene into usd stage `stage`."""
98
128
  root_prim = UsdGeom.Xform.Define(stage, "/root")
99
129
 
100
- category = Ansys.Mechanical.DataModel.Enums.DataModelObjectCategory.Body
101
- bodies = app.DataModel.GetObjectsByType(category)
102
- for body in bodies:
103
- scenegraph_node = Ansys.ACT.Mechanical.Tools.ScenegraphHelpers.GetScenegraph(body)
104
- body_path = root_prim.GetPath().AppendPath(f"body{body.ObjectId}")
105
- _convert_transform_node(scenegraph_node, stage, body_path, bgr_to_rgb_tuple(body.Color))
130
+ for child in scene.Children:
131
+ child: "Ansys.Mechanical.Scenegraph.AttributeNode" = child
132
+ child_path = root_prim.GetPath().AppendPath(child.Tag)
133
+ _convert_attribute_node(child, stage, child_path)
134
+
106
135
 
136
+ def to_usd_stage(app: "ansys.mechanical.core.embedding.App", name: str) -> Usd.Stage:
137
+ """Convert mechanical scene to new usd stage and return it."""
138
+ stage = Usd.Stage.CreateNew(name)
139
+ scene = get_scene(app)
140
+ load_into_usd_stage(scene, stage)
107
141
  return stage
108
142
 
109
143
 
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates.
1
+ # Copyright (C) 2022 - 2025 ANSYS, Inc. and/or its affiliates.
2
2
  # SPDX-License-Identifier: MIT
3
3
  #
4
4
  #
@@ -41,7 +41,8 @@ def _reshape_3cols(arr: np.array, name: str = "array"):
41
41
  """
42
42
  err = f"{name} must be of the form (x0,y0,z0,x1,y1,z1,...,xn,yn,zn).\
43
43
  Given {name} are not divisible by 3!"
44
- assert arr.size % 3 == 0, err
44
+ if arr.size % 3 != 0:
45
+ raise ValueError(err)
45
46
  numrows = int(arr.size / 3)
46
47
  numcols = 3
47
48
  arr = np.reshape(arr, (numrows, numcols))
@@ -59,3 +60,32 @@ def get_nodes_and_coords(tri_tessellation: "Ansys.Mechanical.Scenegraph.TriTesse
59
60
  )
60
61
  np_indices = _reshape_3cols(np.array(tri_tessellation.Indices, dtype=np.int32), "indices")
61
62
  return np_coordinates, np_indices
63
+
64
+
65
+ def get_scene(
66
+ app: "ansys.mechanical.core.embedding.App",
67
+ ) -> "Ansys.Mechanical.Scenegraph.GroupNode":
68
+ """Get the scene of the model."""
69
+ import clr
70
+
71
+ clr.AddReference("Ansys.Mechanical.DataModel")
72
+ clr.AddReference("Ansys.Mechanical.Scenegraph")
73
+ clr.AddReference("Ansys.ACT.Interfaces")
74
+
75
+ import Ansys # isort: skip
76
+
77
+ category = Ansys.Mechanical.DataModel.Enums.DataModelObjectCategory.Body
78
+ group_node = Ansys.Mechanical.Scenegraph.Builders.GroupNodeBuilder()
79
+ for body in app.DataModel.GetObjectsByType(category):
80
+ scenegraph_node = Ansys.ACT.Mechanical.Tools.ScenegraphHelpers.GetScenegraph(body)
81
+ # wrap the body node in an attribute node using the body color
82
+ attribute_node_builder = Ansys.Mechanical.Scenegraph.Builders.AttributeNodeBuilder()
83
+ attribute_node = (
84
+ attribute_node_builder.Tag(f"body{body.ObjectId}")
85
+ .Child(scenegraph_node)
86
+ # set the color, body.Color is a BGR uint bitfield
87
+ .Property(Ansys.Mechanical.Scenegraph.ScenegraphIntAttributes.Color, body.Color)
88
+ .Build()
89
+ )
90
+ group_node.AddChild(attribute_node)
91
+ return group_node.Build()
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates.
1
+ # Copyright (C) 2022 - 2025 ANSYS, Inc. and/or its affiliates.
2
2
  # SPDX-License-Identifier: MIT
3
3
  #
4
4
  #
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates.
1
+ # Copyright (C) 2022 - 2025 ANSYS, Inc. and/or its affiliates.
2
2
  # SPDX-License-Identifier: MIT
3
3
  #
4
4
  #
@@ -31,6 +31,7 @@ from grpc._channel import _InactiveRpcError, _MultiThreadedRendezvous
31
31
  from ansys.mechanical.core import LOG as logger
32
32
 
33
33
  SIGINT_TRACKER = []
34
+ """List of SIGINT keyboard interrupts."""
34
35
 
35
36
 
36
37
  class VersionError(ValueError):
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates.
1
+ # Copyright (C) 2022 - 2025 ANSYS, Inc. and/or its affiliates.
2
2
  # SPDX-License-Identifier: MIT
3
3
  #
4
4
  #
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates.
1
+ # Copyright (C) 2022 - 2025 ANSYS, Inc. and/or its affiliates.
2
2
  # SPDX-License-Identifier: MIT
3
3
  #
4
4
  #
@@ -26,7 +26,8 @@ import os
26
26
  import shutil
27
27
  from typing import Optional
28
28
  from urllib.parse import urljoin
29
- import urllib.request
29
+
30
+ import requests
30
31
 
31
32
  import ansys.mechanical.core as pymechanical
32
33
 
@@ -53,9 +54,13 @@ def _get_filepath_on_default_server(filename: str, *directory: str):
53
54
  return joiner(server, filename)
54
55
 
55
56
 
56
- def _retrieve_url(url, dest):
57
- saved_file, _ = urllib.request.urlretrieve(url, filename=dest)
58
- return saved_file
57
+ def _retrieve_url(url: str, dest: str) -> str:
58
+ with requests.get(url, stream=True, timeout=10) as r:
59
+ r.raise_for_status()
60
+ with open(dest, "wb") as f:
61
+ for chunk in r.iter_content(chunk_size=4096):
62
+ f.write(chunk)
63
+ return dest
59
64
 
60
65
 
61
66
  def _retrieve_data(url: str, filename: str, dest: str = None, force: bool = False):
@@ -0,0 +1,51 @@
1
+ # Copyright (C) 2022 - 2025 ANSYS, Inc. and/or its affiliates.
2
+ # SPDX-License-Identifier: MIT
3
+ #
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ """Mechanical beta feature flags."""
24
+
25
+ import typing
26
+ import warnings
27
+
28
+
29
+ class FeatureFlags:
30
+ """Supported feature flag names."""
31
+
32
+ ThermalShells = "Mechanical.ThermalShells"
33
+ MultistageHarmonic = "Mechanical.MultistageHarmonic"
34
+
35
+
36
+ def get_feature_flag_names() -> typing.List[str]:
37
+ """Get the available feature flags."""
38
+ return [x for x in dir(FeatureFlags) if "_" not in x]
39
+
40
+
41
+ def _get_flag_arg(flagname: str) -> str:
42
+ """Get the command line name for a given feature flag."""
43
+ if hasattr(FeatureFlags, flagname):
44
+ return getattr(FeatureFlags, flagname)
45
+ warnings.warn(f"Using undocumented feature flag {flagname}")
46
+ return flagname
47
+
48
+
49
+ def get_command_line_arguments(flags: typing.List[str]):
50
+ """Get the command line arguments as an array for the given flags."""
51
+ return ["-featureflags", ";".join([_get_flag_arg(flag) for flag in flags])]