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.
- ansys/mechanical/core/__init__.py +11 -4
- ansys/mechanical/core/_version.py +48 -47
- ansys/mechanical/core/embedding/__init__.py +1 -1
- ansys/mechanical/core/embedding/addins.py +1 -7
- ansys/mechanical/core/embedding/app.py +610 -281
- ansys/mechanical/core/embedding/app_libraries.py +24 -5
- ansys/mechanical/core/embedding/appdata.py +16 -4
- ansys/mechanical/core/embedding/background.py +106 -0
- ansys/mechanical/core/embedding/cleanup_gui.py +61 -0
- ansys/mechanical/core/embedding/enum_importer.py +2 -2
- ansys/mechanical/core/embedding/imports.py +27 -7
- ansys/mechanical/core/embedding/initializer.py +105 -53
- ansys/mechanical/core/embedding/loader.py +19 -9
- ansys/mechanical/core/embedding/logger/__init__.py +219 -216
- ansys/mechanical/core/embedding/logger/environ.py +1 -1
- ansys/mechanical/core/embedding/logger/linux_api.py +1 -1
- ansys/mechanical/core/embedding/logger/sinks.py +1 -1
- ansys/mechanical/core/embedding/logger/windows_api.py +2 -2
- ansys/mechanical/core/embedding/poster.py +38 -4
- ansys/mechanical/core/embedding/resolver.py +41 -44
- ansys/mechanical/core/embedding/runtime.py +1 -1
- ansys/mechanical/core/embedding/shims.py +9 -8
- ansys/mechanical/core/embedding/ui.py +228 -0
- ansys/mechanical/core/embedding/utils.py +1 -1
- ansys/mechanical/core/embedding/viz/__init__.py +1 -1
- ansys/mechanical/core/embedding/viz/{pyvista_plotter.py → embedding_plotter.py} +24 -8
- ansys/mechanical/core/embedding/viz/usd_converter.py +59 -25
- ansys/mechanical/core/embedding/viz/utils.py +32 -2
- ansys/mechanical/core/embedding/warnings.py +1 -1
- ansys/mechanical/core/errors.py +2 -1
- ansys/mechanical/core/examples/__init__.py +1 -1
- ansys/mechanical/core/examples/downloads.py +10 -5
- ansys/mechanical/core/feature_flags.py +51 -0
- ansys/mechanical/core/ide_config.py +212 -0
- ansys/mechanical/core/launcher.py +9 -9
- ansys/mechanical/core/logging.py +14 -2
- ansys/mechanical/core/mechanical.py +2324 -2237
- ansys/mechanical/core/misc.py +176 -176
- ansys/mechanical/core/pool.py +712 -712
- ansys/mechanical/core/run.py +321 -246
- {ansys_mechanical_core-0.10.10.dist-info → ansys_mechanical_core-0.11.12.dist-info}/LICENSE +7 -7
- {ansys_mechanical_core-0.10.10.dist-info → ansys_mechanical_core-0.11.12.dist-info}/METADATA +57 -56
- ansys_mechanical_core-0.11.12.dist-info/RECORD +45 -0
- {ansys_mechanical_core-0.10.10.dist-info → ansys_mechanical_core-0.11.12.dist-info}/WHEEL +1 -1
- {ansys_mechanical_core-0.10.10.dist-info → ansys_mechanical_core-0.11.12.dist-info}/entry_points.txt +1 -0
- ansys_mechanical_core-0.10.10.dist-info/RECORD +0 -40
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2022 -
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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 -
|
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
|
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
|
59
|
-
"""
|
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.
|
71
|
-
|
72
|
-
plotter.show()
|
87
|
+
plotter.plot(polydata, color=color, smooth_shading=True)
|
88
|
+
return plotter
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2022 -
|
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
|
-
|
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
|
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
|
-
"""
|
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
|
95
|
-
"
|
96
|
-
stage
|
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
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
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 -
|
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
|
-
|
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()
|
ansys/mechanical/core/errors.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2022 -
|
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 -
|
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
|
-
|
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
|
-
|
58
|
-
|
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])]
|