ansys-mechanical-core 0.10.11__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 +2 -2
- ansys/mechanical/core/embedding/appdata.py +11 -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 +104 -51
- ansys/mechanical/core/embedding/loader.py +1 -1
- 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 +34 -2
- 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 -12
- 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 +1 -1
- 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 -291
- {ansys_mechanical_core-0.10.11.dist-info → ansys_mechanical_core-0.11.12.dist-info}/LICENSE +1 -1
- {ansys_mechanical_core-0.10.11.dist-info → ansys_mechanical_core-0.11.12.dist-info}/METADATA +55 -54
- ansys_mechanical_core-0.11.12.dist-info/RECORD +45 -0
- {ansys_mechanical_core-0.10.11.dist-info → ansys_mechanical_core-0.11.12.dist-info}/WHEEL +1 -1
- {ansys_mechanical_core-0.10.11.dist-info → ansys_mechanical_core-0.11.12.dist-info}/entry_points.txt +1 -0
- ansys_mechanical_core-0.10.11.dist-info/RECORD +0 -41
@@ -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
|
#
|
@@ -74,7 +74,7 @@ def add_mechanical_python_libraries(app_or_version):
|
|
74
74
|
elif isinstance(app_or_version, App):
|
75
75
|
installdir.append(os.environ[f"AWP_ROOT{app_or_version.version}"])
|
76
76
|
else:
|
77
|
-
raise ValueError(
|
77
|
+
raise ValueError("Invalid input: expected an integer (version) or an instance of App().")
|
78
78
|
|
79
79
|
location = os.path.join(installdir[0], "Addins", "ACT", "libraries", "Mechanical")
|
80
80
|
sys.path.append(location)
|
@@ -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,21 +31,28 @@ import warnings
|
|
31
31
|
class UniqueUserProfile:
|
32
32
|
"""Create Unique User Profile (for AppData)."""
|
33
33
|
|
34
|
-
def __init__(self, profile_name: str, dry_run: bool = False):
|
34
|
+
def __init__(self, profile_name: str, copy_profile: bool = True, dry_run: bool = False):
|
35
35
|
"""Initialize UniqueUserProfile class."""
|
36
36
|
self._default_profile = os.path.expanduser("~")
|
37
37
|
self._location = os.path.join(self._default_profile, "PyMechanical-AppData", profile_name)
|
38
38
|
self._dry_run = dry_run
|
39
|
+
self.copy_profile = copy_profile
|
39
40
|
self.initialize()
|
40
41
|
|
41
42
|
def initialize(self) -> None:
|
42
|
-
"""
|
43
|
+
"""
|
44
|
+
Initialize the new profile location.
|
45
|
+
|
46
|
+
Args:
|
47
|
+
copy_profile (bool): If False, the copy_profile method will be skipped.
|
48
|
+
"""
|
43
49
|
if self._dry_run:
|
44
50
|
return
|
45
51
|
if self.exists():
|
46
52
|
self.cleanup()
|
47
53
|
self.mkdirs()
|
48
|
-
self.
|
54
|
+
if self.copy_profile:
|
55
|
+
self.copy_profiles()
|
49
56
|
|
50
57
|
def cleanup(self) -> None:
|
51
58
|
"""Cleanup unique user profile."""
|
@@ -0,0 +1,106 @@
|
|
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
|
+
"""Class for running Mechanical on a background thread."""
|
24
|
+
|
25
|
+
import atexit
|
26
|
+
import threading
|
27
|
+
import time
|
28
|
+
import typing
|
29
|
+
|
30
|
+
import ansys.mechanical.core as mech
|
31
|
+
from ansys.mechanical.core.embedding import initializer
|
32
|
+
from ansys.mechanical.core.embedding.poster import Poster
|
33
|
+
import ansys.mechanical.core.embedding.utils as utils
|
34
|
+
|
35
|
+
|
36
|
+
def _exit(background_app: "BackgroundApp"):
|
37
|
+
"""Stop the thread serving the Background App."""
|
38
|
+
background_app.stop()
|
39
|
+
|
40
|
+
|
41
|
+
class BackgroundApp:
|
42
|
+
"""Background App."""
|
43
|
+
|
44
|
+
__app: mech.App = None
|
45
|
+
__app_thread: threading.Thread = None
|
46
|
+
__stopped: bool = False
|
47
|
+
__stop_signaled: bool = False
|
48
|
+
__poster: Poster = None
|
49
|
+
|
50
|
+
def __init__(self, **kwargs):
|
51
|
+
"""Construct an instance of BackgroundApp."""
|
52
|
+
if BackgroundApp.__app_thread is None:
|
53
|
+
initializer.initialize(kwargs.get("version"))
|
54
|
+
BackgroundApp.__app_thread = threading.Thread(
|
55
|
+
target=self._start_app, kwargs=kwargs, daemon=True
|
56
|
+
)
|
57
|
+
BackgroundApp.__app_thread.start()
|
58
|
+
|
59
|
+
while BackgroundApp.__poster is None:
|
60
|
+
time.sleep(0.05)
|
61
|
+
continue
|
62
|
+
else:
|
63
|
+
if BackgroundApp.__stopped:
|
64
|
+
raise RuntimeError("Cannot initialize a BackgroundApp once it has been stopped!")
|
65
|
+
|
66
|
+
def new():
|
67
|
+
BackgroundApp.__app.new()
|
68
|
+
|
69
|
+
self.post(new)
|
70
|
+
|
71
|
+
@property
|
72
|
+
def app(self) -> mech.App:
|
73
|
+
"""Get the App instance of the background thread.
|
74
|
+
|
75
|
+
It is not meant to be used aside from passing to methods using `post`.
|
76
|
+
"""
|
77
|
+
return BackgroundApp.__app
|
78
|
+
|
79
|
+
def post(self, callable: typing.Callable):
|
80
|
+
"""Post callable method to the background app thread."""
|
81
|
+
if BackgroundApp.__stopped:
|
82
|
+
raise RuntimeError("Cannot use BackgroundApp after stopping it.")
|
83
|
+
return BackgroundApp.__poster.post(callable)
|
84
|
+
|
85
|
+
def stop(self) -> None:
|
86
|
+
"""Stop the background app thread."""
|
87
|
+
if BackgroundApp.__stopped:
|
88
|
+
return
|
89
|
+
BackgroundApp.__stop_signaled = True
|
90
|
+
while True:
|
91
|
+
time.sleep(0.05)
|
92
|
+
if BackgroundApp.__stopped:
|
93
|
+
break
|
94
|
+
|
95
|
+
def _start_app(self, **kwargs) -> None:
|
96
|
+
BackgroundApp.__app = mech.App(**kwargs)
|
97
|
+
BackgroundApp.__poster = BackgroundApp.__app.poster
|
98
|
+
atexit.register(_exit, self)
|
99
|
+
while True:
|
100
|
+
if BackgroundApp.__stop_signaled:
|
101
|
+
break
|
102
|
+
try:
|
103
|
+
utils.sleep(40)
|
104
|
+
except:
|
105
|
+
raise Exception("BackgroundApp cannot sleep.") # pragma: no cover
|
106
|
+
BackgroundApp.__stopped = True
|
@@ -0,0 +1,61 @@
|
|
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
|
+
"""Clean up temporary mechdb files after GUI is closed."""
|
23
|
+
|
24
|
+
from pathlib import Path
|
25
|
+
import shutil
|
26
|
+
import sys
|
27
|
+
import time
|
28
|
+
|
29
|
+
import psutil
|
30
|
+
|
31
|
+
|
32
|
+
def cleanup_gui(pid, temp_mechdb) -> None:
|
33
|
+
"""Remove the temporary mechdb file after it is closed.
|
34
|
+
|
35
|
+
Parameters
|
36
|
+
----------
|
37
|
+
pid: int
|
38
|
+
The process ID of the open temporary mechdb file.
|
39
|
+
temp_mechdb: Path
|
40
|
+
The path of the temporary mechdb file.
|
41
|
+
"""
|
42
|
+
# While the pid exists, sleep
|
43
|
+
while psutil.pid_exists(pid):
|
44
|
+
time.sleep(1)
|
45
|
+
|
46
|
+
# Delete the temporary mechdb file once the GUI is closed
|
47
|
+
temp_mechdb.unlink()
|
48
|
+
|
49
|
+
# Delete the temporary mechdb Mech_Files folder
|
50
|
+
temp_folder_path = temp_mechdb.parent / f"{temp_mechdb.name.split('.')[0]}_Mech_Files"
|
51
|
+
shutil.rmtree(temp_folder_path)
|
52
|
+
|
53
|
+
|
54
|
+
if __name__ == "__main__": # pragma: no cover
|
55
|
+
"""Get the process ID and temporary file path to monitor and delete files after use."""
|
56
|
+
# Convert the process id (pid) argument into an integer
|
57
|
+
pid = int(sys.argv[1])
|
58
|
+
# Convert the temporary mechdb path into a Path
|
59
|
+
temp_mechdb_path = Path(sys.argv[2])
|
60
|
+
# Remove the temporary mechdb file when the GUI is closed
|
61
|
+
cleanup_gui(pid, temp_mechdb_path)
|
@@ -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
|
#
|
@@ -33,5 +33,5 @@ clr.AddReference("Ansys.ACT.Interfaces")
|
|
33
33
|
|
34
34
|
from Ansys.ACT.Interfaces.Common import * # noqa isort: skip
|
35
35
|
from Ansys.Mechanical.DataModel.Enums import * # noqa isort: skip
|
36
|
-
|
36
|
+
from Ansys.ACT.Interfaces.Analysis import * # noqa isort: skip
|
37
37
|
import Ansys # noqa isort: skip
|
@@ -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,20 +24,28 @@
|
|
24
24
|
import typing
|
25
25
|
|
26
26
|
|
27
|
+
def global_entry_points(app: "ansys.mechanical.core.App") -> typing.Dict:
|
28
|
+
"""Return the global entry points of the application."""
|
29
|
+
vars = {}
|
30
|
+
vars["ExtAPI"] = app.ExtAPI
|
31
|
+
vars["DataModel"] = app.DataModel
|
32
|
+
vars["Model"] = app.DataModel.Project.Model
|
33
|
+
vars["Tree"] = app.DataModel.Tree
|
34
|
+
vars["Graphics"] = app.ExtAPI.Graphics
|
35
|
+
return vars
|
36
|
+
|
37
|
+
|
27
38
|
def global_variables(app: "ansys.mechanical.core.App", enums: bool = False) -> typing.Dict:
|
28
39
|
"""Return the Mechanical scripting global variables as a dict.
|
29
40
|
|
30
41
|
It can be used to add all of these as global variables in python
|
31
42
|
with this command:
|
32
|
-
|
43
|
+
|
44
|
+
``globals().update(global_variables(embedded_app))``
|
33
45
|
|
34
46
|
To also import all the enums, set the parameter enums to true.
|
35
47
|
"""
|
36
|
-
vars =
|
37
|
-
vars["ExtAPI"] = app.ExtAPI
|
38
|
-
vars["DataModel"] = app.DataModel
|
39
|
-
vars["Model"] = app.DataModel.Project.Model
|
40
|
-
vars["Tree"] = app.DataModel.Tree
|
48
|
+
vars = global_entry_points(app)
|
41
49
|
import clr # isort: skip
|
42
50
|
|
43
51
|
clr.AddReference("System.Collections")
|
@@ -45,8 +53,12 @@ def global_variables(app: "ansys.mechanical.core.App", enums: bool = False) -> t
|
|
45
53
|
clr.AddReference("Ansys.Mechanical.DataModel")
|
46
54
|
# from Ansys.ACT.Mechanical import Transaction
|
47
55
|
# When ansys-pythonnet issue #14 is fixed, uncomment above
|
56
|
+
from Ansys.ACT.Core.Math import Point2D, Point3D
|
57
|
+
from Ansys.ACT.Math import Vector3D
|
58
|
+
from Ansys.ACT.Mechanical.Fields import VariableDefinitionType
|
48
59
|
from Ansys.Core.Units import Quantity
|
49
60
|
from Ansys.Mechanical.DataModel import MechanicalEnums
|
61
|
+
from Ansys.Mechanical.Graphics import Point, SectionPlane
|
50
62
|
|
51
63
|
import System # isort: skip
|
52
64
|
import Ansys # isort: skip
|
@@ -56,6 +68,14 @@ def global_variables(app: "ansys.mechanical.core.App", enums: bool = False) -> t
|
|
56
68
|
vars["Ansys"] = Ansys
|
57
69
|
vars["Transaction"] = Transaction
|
58
70
|
vars["MechanicalEnums"] = MechanicalEnums
|
71
|
+
# Graphics
|
72
|
+
vars["Point"] = Point
|
73
|
+
vars["SectionPlane"] = SectionPlane
|
74
|
+
# Math
|
75
|
+
vars["Point2D"] = Point2D
|
76
|
+
vars["Point3D"] = Point3D
|
77
|
+
vars["Vector3D"] = Vector3D
|
78
|
+
vars["VariableDefinitionType"] = VariableDefinitionType
|
59
79
|
|
60
80
|
if enums:
|
61
81
|
vars.update(get_all_enums())
|
@@ -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
|
#
|
@@ -28,13 +28,19 @@ import platform
|
|
28
28
|
import sys
|
29
29
|
import warnings
|
30
30
|
|
31
|
-
import ansys.tools.path as atp
|
32
|
-
|
33
31
|
from ansys.mechanical.core.embedding.loader import load_clr
|
34
32
|
from ansys.mechanical.core.embedding.resolver import resolve
|
35
33
|
|
36
34
|
INITIALIZED_VERSION = None
|
37
|
-
|
35
|
+
"""Constant for the initialized version."""
|
36
|
+
|
37
|
+
SUPPORTED_MECHANICAL_EMBEDDING_VERSIONS = {
|
38
|
+
251: "2025R1",
|
39
|
+
242: "2024R2",
|
40
|
+
241: "2024R1",
|
41
|
+
232: "2023R2",
|
42
|
+
}
|
43
|
+
"""Supported Mechanical embedding versions on Windows."""
|
38
44
|
|
39
45
|
|
40
46
|
def __add_sys_path(version: int) -> str:
|
@@ -44,15 +50,6 @@ def __add_sys_path(version: int) -> str:
|
|
44
50
|
sys.path.append(str(bin_path.resolve()))
|
45
51
|
|
46
52
|
|
47
|
-
def __disable_sec() -> None:
|
48
|
-
"""SEC is part of RSM and is unstable with embedding.
|
49
|
-
|
50
|
-
I'm not going to debug why that is since we are planning to support
|
51
|
-
DCS/REP in the future instead of RSM.
|
52
|
-
"""
|
53
|
-
os.environ["ANSYS_MECHANICAL_EMBEDDING_NO_SEC"] = "1"
|
54
|
-
|
55
|
-
|
56
53
|
def __workaround_material_server(version: int) -> None:
|
57
54
|
"""Workaround material server bug in 2024 R1.
|
58
55
|
|
@@ -65,48 +62,68 @@ def __workaround_material_server(version: int) -> None:
|
|
65
62
|
os.environ["ENGRDATA_SERVER_SERIAL"] = "1"
|
66
63
|
|
67
64
|
|
68
|
-
def
|
69
|
-
"""
|
65
|
+
def __check_for_supported_version(version) -> None:
|
66
|
+
"""Check if Mechanical version is supported with current version of PyMechanical.
|
70
67
|
|
71
|
-
|
72
|
-
|
73
|
-
The documented way to set those variables is to run python using the ``mechanical-env`` script,
|
74
|
-
which can be used after installing the ``ansys-mechanical-env`` package with this command:
|
75
|
-
``pip install ansys-mechanical-env``. The script takes user input of a version. If the user
|
76
|
-
does not provide a version, the ``find_mechanical()`` function from the ``ansys-tools-path``
|
77
|
-
package is used to find a version of Mechanical.
|
68
|
+
If specific environment variable is enabled, then users can overwrite the supported versions.
|
69
|
+
However, using unsupported versions may cause issues.
|
78
70
|
"""
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
return
|
71
|
+
allow_old_version = os.getenv("ANSYS_MECHANICAL_EMBEDDING_SUPPORT_OLD_VERSIONS") == "1"
|
72
|
+
|
73
|
+
# Check if the version is supported
|
74
|
+
if not allow_old_version and version < min(SUPPORTED_MECHANICAL_EMBEDDING_VERSIONS):
|
75
|
+
raise ValueError(f"Mechanical version {version} is not supported.")
|
76
|
+
|
77
|
+
return version
|
78
|
+
|
86
79
|
|
80
|
+
def _get_latest_default_version() -> int:
|
81
|
+
"""Try to get the latest Mechanical version from the environment.
|
87
82
|
|
88
|
-
|
89
|
-
|
90
|
-
|
83
|
+
Checks if multiple versions of Mechanical found in system.
|
84
|
+
For Linux it will be only one since ``mechanical-env`` takes care of that.
|
85
|
+
If multiple versions are detected, select the latest one, as no specific version is provided.
|
86
|
+
"""
|
87
|
+
awp_roots = [value for key, value in os.environ.items() if key.startswith("AWP_ROOT")]
|
91
88
|
|
92
|
-
if
|
93
|
-
raise Exception("
|
89
|
+
if not awp_roots:
|
90
|
+
raise Exception("No Mechanical installations found.")
|
94
91
|
|
95
|
-
|
96
|
-
|
97
|
-
|
92
|
+
versions_found = []
|
93
|
+
for path in awp_roots:
|
94
|
+
folder = os.path.basename(os.path.normpath(path))
|
95
|
+
version = folder.split("v")[-1]
|
96
|
+
versions_found.append(int(version))
|
97
|
+
latest_version = max(versions_found)
|
98
98
|
|
99
|
-
|
100
|
-
|
101
|
-
|
99
|
+
if len(awp_roots) > 1:
|
100
|
+
warnings.warn(
|
101
|
+
f"Multiple versions of Mechanical found! Using latest version {latest_version} ..."
|
102
|
+
)
|
102
103
|
|
104
|
+
return latest_version
|
103
105
|
|
104
|
-
|
106
|
+
|
107
|
+
def __check_python_interpreter_architecture() -> None:
|
105
108
|
"""Embedding support only 64 bit architecture."""
|
106
109
|
if platform.architecture()[0] != "64bit":
|
107
110
|
raise Exception("Mechanical Embedding requires a 64-bit Python environment.")
|
108
111
|
|
109
112
|
|
113
|
+
def __set_environment(version: int) -> None:
|
114
|
+
"""Set environment variables to configure embedding."""
|
115
|
+
if os.name == "nt": # pragma: no cover
|
116
|
+
if version < 251:
|
117
|
+
os.environ["MECHANICAL_STARTUP_UNOPTIMIZED"] = "1"
|
118
|
+
|
119
|
+
# Set an environment variable to use the custom CLR host
|
120
|
+
# for embedding.
|
121
|
+
# In the future (>251), it would always be used.
|
122
|
+
if version == 251:
|
123
|
+
if "PYMECHANICAL_NO_CLR_HOST_LITE" not in os.environ:
|
124
|
+
os.environ["ANSYS_MECHANICAL_EMBEDDING_CLR_HOST"] = "1"
|
125
|
+
|
126
|
+
|
110
127
|
def __check_for_mechanical_env():
|
111
128
|
"""Embedding in linux platform must use mechanical-env."""
|
112
129
|
if platform.system() == "Linux" and os.environ.get("PYMECHANICAL_EMBEDDING") != "TRUE":
|
@@ -119,22 +136,57 @@ def __check_for_mechanical_env():
|
|
119
136
|
)
|
120
137
|
|
121
138
|
|
139
|
+
def __is_lib_loaded(libname: str): # pragma: no cover
|
140
|
+
"""Return whether a library is loaded."""
|
141
|
+
import ctypes
|
142
|
+
|
143
|
+
RTLD_NOLOAD = 4
|
144
|
+
try:
|
145
|
+
ctypes.CDLL(libname, RTLD_NOLOAD)
|
146
|
+
except OSError:
|
147
|
+
return False
|
148
|
+
return True
|
149
|
+
|
150
|
+
|
151
|
+
def __check_loaded_libs(version: int = None): # pragma: no cover
|
152
|
+
"""Ensure that incompatible libraries aren't loaded prior to PyMechanical load."""
|
153
|
+
if platform.system() != "Linux":
|
154
|
+
return
|
155
|
+
|
156
|
+
if version < 251:
|
157
|
+
return
|
158
|
+
|
159
|
+
# For 2025 R1, PyMechanical will crash on shutdown if libX11.so is already loaded
|
160
|
+
# before starting Mechanical
|
161
|
+
if __is_lib_loaded("libX11.so"):
|
162
|
+
warnings.warn(
|
163
|
+
"libX11.so is loaded prior to initializing the Embedded Instance of Mechanical.\
|
164
|
+
Python will crash on shutdown..."
|
165
|
+
)
|
166
|
+
|
167
|
+
|
122
168
|
def initialize(version: int = None):
|
123
169
|
"""Initialize Mechanical embedding."""
|
124
|
-
__check_python_interpreter_architecture() # blocks 32 bit python
|
125
|
-
__check_for_mechanical_env() # checks for mechanical-env in linux embedding
|
126
|
-
|
127
170
|
global INITIALIZED_VERSION
|
128
|
-
if
|
129
|
-
|
130
|
-
return
|
171
|
+
if version is None:
|
172
|
+
version = _get_latest_default_version()
|
131
173
|
|
132
|
-
|
133
|
-
version = _get_default_version()
|
174
|
+
version = __check_for_supported_version(version=version)
|
134
175
|
|
135
|
-
INITIALIZED_VERSION
|
176
|
+
if INITIALIZED_VERSION is not None:
|
177
|
+
if INITIALIZED_VERSION != version:
|
178
|
+
raise ValueError(
|
179
|
+
f"Initialized version {INITIALIZED_VERSION} "
|
180
|
+
f"does not match the expected version {version}."
|
181
|
+
)
|
182
|
+
return INITIALIZED_VERSION
|
136
183
|
|
137
|
-
|
184
|
+
__check_python_interpreter_architecture() # blocks 32 bit python
|
185
|
+
__check_for_mechanical_env() # checks for mechanical-env in linux embedding
|
186
|
+
|
187
|
+
__set_environment(version)
|
188
|
+
|
189
|
+
__check_loaded_libs(version)
|
138
190
|
|
139
191
|
__workaround_material_server(version)
|
140
192
|
|
@@ -163,4 +215,5 @@ def initialize(version: int = None):
|
|
163
215
|
# attach the resolver
|
164
216
|
resolve(version)
|
165
217
|
|
218
|
+
INITIALIZED_VERSION = version
|
166
219
|
return version
|