ansys-mechanical-core 0.11.13__py3-none-any.whl → 0.11.15__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 +3 -4
- ansys/mechanical/core/embedding/app.py +97 -12
- ansys/mechanical/core/embedding/appdata.py +26 -22
- ansys/mechanical/core/embedding/enum_importer.py +5 -0
- ansys/mechanical/core/embedding/global_importer.py +50 -0
- ansys/mechanical/core/embedding/{viz → graphics}/embedding_plotter.py +1 -1
- ansys/mechanical/core/embedding/imports.py +30 -58
- ansys/mechanical/core/embedding/initializer.py +76 -4
- ansys/mechanical/core/embedding/messages.py +195 -0
- ansys/mechanical/core/embedding/resolver.py +1 -1
- ansys/mechanical/core/embedding/rpc/__init__.py +3 -7
- ansys/mechanical/core/embedding/rpc/client.py +55 -19
- ansys/mechanical/core/embedding/rpc/default_server.py +131 -0
- ansys/mechanical/core/embedding/rpc/server.py +171 -162
- ansys/mechanical/core/embedding/rpc/utils.py +18 -2
- ansys/mechanical/core/embedding/runtime.py +6 -0
- ansys/mechanical/core/embedding/transaction.py +51 -0
- ansys/mechanical/core/ide_config.py +22 -7
- ansys/mechanical/core/mechanical.py +86 -18
- {ansys_mechanical_core-0.11.13.dist-info → ansys_mechanical_core-0.11.15.dist-info}/METADATA +21 -17
- ansys_mechanical_core-0.11.15.dist-info/RECORD +53 -0
- {ansys_mechanical_core-0.11.13.dist-info → ansys_mechanical_core-0.11.15.dist-info}/WHEEL +1 -1
- ansys_mechanical_core-0.11.13.dist-info/RECORD +0 -49
- /ansys/mechanical/core/embedding/{viz → graphics}/__init__.py +0 -0
- /ansys/mechanical/core/embedding/{viz → graphics}/usd_converter.py +0 -0
- /ansys/mechanical/core/embedding/{viz → graphics}/utils.py +0 -0
- {ansys_mechanical_core-0.11.13.dist-info → ansys_mechanical_core-0.11.15.dist-info}/entry_points.txt +0 -0
- {ansys_mechanical_core-0.11.13.dist-info → ansys_mechanical_core-0.11.15.dist-info/licenses}/LICENSE +0 -0
@@ -44,10 +44,12 @@ from ansys.mechanical.core.logging import Logger
|
|
44
44
|
LOG = Logger(level=logging.ERROR, to_file=False, to_stdout=True)
|
45
45
|
"""Create logger for package level use."""
|
46
46
|
|
47
|
-
LOG.debug("Loaded logging module as LOG")
|
48
47
|
|
49
48
|
from ansys.mechanical.core._version import __version__
|
49
|
+
|
50
|
+
# import few classes / functions
|
50
51
|
from ansys.mechanical.core.mechanical import (
|
52
|
+
Mechanical,
|
51
53
|
change_default_mechanical_path,
|
52
54
|
close_all_local_instances,
|
53
55
|
connect_to_mechanical,
|
@@ -55,9 +57,6 @@ from ansys.mechanical.core.mechanical import (
|
|
55
57
|
launch_mechanical,
|
56
58
|
)
|
57
59
|
|
58
|
-
# import few classes / functions
|
59
|
-
from ansys.mechanical.core.mechanical import Mechanical as Mechanical
|
60
|
-
|
61
60
|
try:
|
62
61
|
from ansys.mechanical.core.embedding import App, global_variables
|
63
62
|
|
@@ -30,6 +30,7 @@ import shutil
|
|
30
30
|
import typing
|
31
31
|
import warnings
|
32
32
|
|
33
|
+
from ansys.mechanical.core import LOG
|
33
34
|
from ansys.mechanical.core.embedding import initializer, runtime
|
34
35
|
from ansys.mechanical.core.embedding.addins import AddinConfiguration
|
35
36
|
from ansys.mechanical.core.embedding.appdata import UniqueUserProfile
|
@@ -46,10 +47,10 @@ if typing.TYPE_CHECKING:
|
|
46
47
|
try:
|
47
48
|
import ansys.tools.visualization_interface # noqa: F401
|
48
49
|
|
49
|
-
|
50
|
+
HAS_ANSYS_GRAPHICS = True
|
50
51
|
"""Whether or not PyVista exists."""
|
51
52
|
except ImportError:
|
52
|
-
|
53
|
+
HAS_ANSYS_GRAPHICS = False
|
53
54
|
|
54
55
|
|
55
56
|
def _get_default_addin_configuration() -> AddinConfiguration:
|
@@ -89,6 +90,11 @@ def _start_application(configuration: AddinConfiguration, version, db_file) -> "
|
|
89
90
|
return Ansys.Mechanical.Embedding.Application(db_file)
|
90
91
|
|
91
92
|
|
93
|
+
def is_initialized():
|
94
|
+
"""Check if the app has been initialized."""
|
95
|
+
return len(INSTANCES) != 0
|
96
|
+
|
97
|
+
|
92
98
|
class GetterWrapper(object):
|
93
99
|
"""Wrapper class around an attribute of an object."""
|
94
100
|
|
@@ -124,10 +130,17 @@ class App:
|
|
124
130
|
private_appdata : bool, optional
|
125
131
|
Setting for a temporary AppData directory. Default is False.
|
126
132
|
Enables running parallel instances of Mechanical.
|
133
|
+
globals : dict, optional
|
134
|
+
Global variables to be updated. For example, globals().
|
135
|
+
Replaces "app.update_globals(globals())".
|
127
136
|
config : AddinConfiguration, optional
|
128
137
|
Configuration for addins. By default "Mechanical" is used and ACT Addins are disabled.
|
129
138
|
copy_profile : bool, optional
|
130
139
|
Whether to copy the user profile when private_appdata is True. Default is True.
|
140
|
+
enable_logging : bool, optional
|
141
|
+
Whether to enable logging. Default is True.
|
142
|
+
log_level : str, optional
|
143
|
+
The logging level for the application. Default is "WARNING".
|
131
144
|
|
132
145
|
Examples
|
133
146
|
--------
|
@@ -140,6 +153,10 @@ class App:
|
|
140
153
|
|
141
154
|
>>> app = App(private_appdata=True, copy_profile=False)
|
142
155
|
|
156
|
+
Update the global variables with globals
|
157
|
+
|
158
|
+
>>> app = App(globals=globals())
|
159
|
+
|
143
160
|
Create App with "Mechanical" configuration and no ACT Addins
|
144
161
|
|
145
162
|
>>> from ansys.mechanical.core.embedding import AddinConfiguration
|
@@ -147,6 +164,13 @@ class App:
|
|
147
164
|
>>> config = AddinConfiguration("Mechanical")
|
148
165
|
>>> config.no_act_addins = True
|
149
166
|
>>> app = App(config=config)
|
167
|
+
|
168
|
+
Set log level
|
169
|
+
|
170
|
+
>>> app = App(log_level='INFO')
|
171
|
+
|
172
|
+
... INFO - - app - log_info - Starting Mechanical Application
|
173
|
+
|
150
174
|
"""
|
151
175
|
|
152
176
|
def __init__(self, db_file=None, private_appdata=False, **kwargs):
|
@@ -154,15 +178,36 @@ class App:
|
|
154
178
|
global INSTANCES
|
155
179
|
from ansys.mechanical.core import BUILDING_GALLERY
|
156
180
|
|
181
|
+
self._enable_logging = kwargs.get("enable_logging", True)
|
182
|
+
if self._enable_logging:
|
183
|
+
self._log = LOG
|
184
|
+
self._log_level = kwargs.get("log_level", "WARNING")
|
185
|
+
self._log.setLevel(self._log_level)
|
186
|
+
|
187
|
+
self.log_info("Starting Mechanical Application")
|
188
|
+
|
189
|
+
# Get the globals dictionary from kwargs
|
190
|
+
globals = kwargs.get("globals")
|
191
|
+
|
192
|
+
# If the building gallery flag is set, we need to share the instance
|
193
|
+
# This can apply to running the `make -C doc html` command
|
157
194
|
if BUILDING_GALLERY:
|
158
195
|
if len(INSTANCES) != 0:
|
196
|
+
# Get the first instance of the app
|
159
197
|
instance: App = INSTANCES[0]
|
198
|
+
# Point to the same underlying application object
|
160
199
|
instance._share(self)
|
200
|
+
# Update the globals if provided in kwargs
|
201
|
+
if globals:
|
202
|
+
# The next line is covered by test_globals_kwarg_building_gallery
|
203
|
+
instance.update_globals(globals) # pragma: nocover
|
204
|
+
# Open the mechdb file if provided
|
161
205
|
if db_file is not None:
|
162
206
|
self.open(db_file)
|
163
207
|
return
|
164
208
|
if len(INSTANCES) > 0:
|
165
209
|
raise Exception("Cannot have more than one embedded mechanical instance!")
|
210
|
+
|
166
211
|
version = kwargs.get("version")
|
167
212
|
if version is not None:
|
168
213
|
try:
|
@@ -179,7 +224,6 @@ class App:
|
|
179
224
|
new_profile_name = f"PyMechanical-{os.getpid()}"
|
180
225
|
profile = UniqueUserProfile(new_profile_name, copy_profile=copy_profile)
|
181
226
|
profile.update_environment(os.environ)
|
182
|
-
atexit.register(_cleanup_private_appdata, profile)
|
183
227
|
|
184
228
|
runtime.initialize(self._version)
|
185
229
|
self._app = _start_application(configuration, self._version, db_file)
|
@@ -189,8 +233,16 @@ class App:
|
|
189
233
|
self._disposed = False
|
190
234
|
atexit.register(_dispose_embedded_app, INSTANCES)
|
191
235
|
INSTANCES.append(self)
|
236
|
+
|
237
|
+
# Clean up the private appdata directory on exit if private_appdata is True
|
238
|
+
if private_appdata:
|
239
|
+
atexit.register(_cleanup_private_appdata, profile)
|
240
|
+
|
192
241
|
self._updated_scopes: typing.List[typing.Dict[str, typing.Any]] = []
|
193
242
|
self._subscribe()
|
243
|
+
self._messages = None
|
244
|
+
if globals:
|
245
|
+
self.update_globals(globals)
|
194
246
|
|
195
247
|
def __repr__(self):
|
196
248
|
"""Get the product info."""
|
@@ -227,6 +279,7 @@ class App:
|
|
227
279
|
remove_lock : bool, optional
|
228
280
|
Whether or not to remove the lock file if it exists before opening the project file.
|
229
281
|
"""
|
282
|
+
self.log_info(f"Opening {db_file} ...")
|
230
283
|
if remove_lock:
|
231
284
|
lock_file = Path(self.DataModel.Project.ProjectDirectory) / ".mech_lock"
|
232
285
|
# Remove the lock file if it exists before opening the project file
|
@@ -352,27 +405,27 @@ This may corrupt the project file.",
|
|
352
405
|
|
353
406
|
def plotter(self) -> None:
|
354
407
|
"""Return ``ansys.tools.visualization_interface.Plotter`` object."""
|
355
|
-
if not
|
356
|
-
|
357
|
-
"
|
408
|
+
if not HAS_ANSYS_GRAPHICS:
|
409
|
+
LOG.warning(
|
410
|
+
"Use ``pip install ansys-mechanical-core[graphics]`` to enable this option."
|
358
411
|
)
|
359
412
|
return
|
360
413
|
|
361
414
|
if self.version < 242:
|
362
|
-
|
415
|
+
LOG.warning("Plotting is only supported with version 2024R2 and later!")
|
363
416
|
return
|
364
417
|
|
365
418
|
# TODO Check if anything loaded inside app or else show warning and return
|
366
419
|
|
367
|
-
from ansys.mechanical.core.embedding.
|
420
|
+
from ansys.mechanical.core.embedding.graphics.embedding_plotter import to_plotter
|
368
421
|
|
369
422
|
return to_plotter(self)
|
370
423
|
|
371
424
|
def plot(self) -> None:
|
372
425
|
"""Visualize the model in 3d.
|
373
426
|
|
374
|
-
Requires installation using the
|
375
|
-
pip install ansys-mechanical-core[
|
427
|
+
Requires installation using the graphics option. E.g.
|
428
|
+
pip install ansys-mechanical-core[graphics]
|
376
429
|
|
377
430
|
Examples
|
378
431
|
--------
|
@@ -437,6 +490,15 @@ This may corrupt the project file.",
|
|
437
490
|
"""Returns the current project directory."""
|
438
491
|
return self.DataModel.Project.ProjectDirectory
|
439
492
|
|
493
|
+
@property
|
494
|
+
def messages(self):
|
495
|
+
"""Lazy-load the MessageManager."""
|
496
|
+
if self._messages is None:
|
497
|
+
from ansys.mechanical.core.embedding.messages import MessageManager
|
498
|
+
|
499
|
+
self._messages = MessageManager(self._app)
|
500
|
+
return self._messages
|
501
|
+
|
440
502
|
def _share(self, other) -> None:
|
441
503
|
"""Shares the state of self with other.
|
442
504
|
|
@@ -580,8 +642,7 @@ This may corrupt the project file.",
|
|
580
642
|
Examples
|
581
643
|
--------
|
582
644
|
>>> from ansys.mechanical.core import App
|
583
|
-
>>> app = App()
|
584
|
-
>>> app.update_globals(globals())
|
645
|
+
>>> app = App(globals=globals())
|
585
646
|
>>> app.print_tree()
|
586
647
|
... ├── Project
|
587
648
|
... | ├── Model
|
@@ -608,3 +669,27 @@ This may corrupt the project file.",
|
|
608
669
|
node = self.DataModel.Project
|
609
670
|
|
610
671
|
self._print_tree(node, max_lines, lines_count, indentation)
|
672
|
+
|
673
|
+
def log_debug(self, message):
|
674
|
+
"""Log the debug message."""
|
675
|
+
if not self._enable_logging:
|
676
|
+
return
|
677
|
+
self._log.debug(message)
|
678
|
+
|
679
|
+
def log_info(self, message):
|
680
|
+
"""Log the info message."""
|
681
|
+
if not self._enable_logging:
|
682
|
+
return
|
683
|
+
self._log.info(message)
|
684
|
+
|
685
|
+
def log_warning(self, message):
|
686
|
+
"""Log the warning message."""
|
687
|
+
if not self._enable_logging:
|
688
|
+
return
|
689
|
+
self._log.warning(message)
|
690
|
+
|
691
|
+
def log_error(self, message):
|
692
|
+
"""Log the error message."""
|
693
|
+
if not self._enable_logging:
|
694
|
+
return
|
695
|
+
self._log.error(message)
|
@@ -22,7 +22,7 @@
|
|
22
22
|
|
23
23
|
"""Temporary Appdata for Ansys Mechanical."""
|
24
24
|
|
25
|
-
import
|
25
|
+
from pathlib import Path
|
26
26
|
import shutil
|
27
27
|
import sys
|
28
28
|
import warnings
|
@@ -33,8 +33,8 @@ class UniqueUserProfile:
|
|
33
33
|
|
34
34
|
def __init__(self, profile_name: str, copy_profile: bool = True, dry_run: bool = False):
|
35
35
|
"""Initialize UniqueUserProfile class."""
|
36
|
-
self._default_profile =
|
37
|
-
self._location =
|
36
|
+
self._default_profile = Path("~").expanduser()
|
37
|
+
self._location = self._default_profile / "PyMechanical-AppData" / profile_name
|
38
38
|
self._dry_run = dry_run
|
39
39
|
self.copy_profile = copy_profile
|
40
40
|
self.initialize()
|
@@ -58,15 +58,14 @@ class UniqueUserProfile:
|
|
58
58
|
"""Cleanup unique user profile."""
|
59
59
|
if self._dry_run:
|
60
60
|
return
|
61
|
-
text = "The `private_appdata` option was used, but the following files were not removed: "
|
62
|
-
message = []
|
63
61
|
|
64
|
-
|
65
|
-
|
66
|
-
message.append(f"{text}{path}")
|
67
|
-
warnings.warn(message[0])
|
62
|
+
# Remove the appdata directory if it exists
|
63
|
+
shutil.rmtree(self.location, ignore_errors=True)
|
68
64
|
|
69
|
-
|
65
|
+
if self.location.is_dir():
|
66
|
+
warnings.warn(
|
67
|
+
f"The `private appdata` option was used, but {self.location} was not removed"
|
68
|
+
)
|
70
69
|
|
71
70
|
@property
|
72
71
|
def location(self) -> str:
|
@@ -77,28 +76,32 @@ class UniqueUserProfile:
|
|
77
76
|
"""Set environment variables for new user profile."""
|
78
77
|
home = self.location
|
79
78
|
if "win" in sys.platform:
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
env["
|
84
|
-
env["
|
79
|
+
appdata_dir = home / "AppData"
|
80
|
+
appdata_local_temp = str(appdata_dir / "Local" / "Temp")
|
81
|
+
|
82
|
+
env["USERPROFILE"] = str(home)
|
83
|
+
env["APPDATA"] = str(appdata_dir / "Roaming")
|
84
|
+
env["LOCALAPPDATA"] = str(appdata_dir / "Local")
|
85
|
+
env["TMP"] = appdata_local_temp
|
86
|
+
env["TEMP"] = appdata_local_temp
|
85
87
|
elif "lin" in sys.platform:
|
86
|
-
env["HOME"] = home
|
88
|
+
env["HOME"] = str(home)
|
87
89
|
|
88
90
|
def exists(self) -> bool:
|
89
91
|
"""Check if unique profile name already exists."""
|
90
|
-
return
|
92
|
+
return self.location.exists()
|
91
93
|
|
92
94
|
def mkdirs(self) -> None:
|
93
95
|
"""Create a unique user profile & set up the directory tree."""
|
94
|
-
|
96
|
+
self.location.mkdir(parents=True, exist_ok=True)
|
95
97
|
if "win" in sys.platform:
|
96
98
|
locs = ["AppData/Roaming", "AppData/Local", "Documents"]
|
97
99
|
elif "lin" in sys.platform:
|
98
100
|
locs = [".config", "temp/reports"]
|
99
101
|
|
100
102
|
for loc in locs:
|
101
|
-
|
103
|
+
dir_name = self.location / loc
|
104
|
+
dir_name.mkdir(parents=True, exist_ok=True)
|
102
105
|
|
103
106
|
def copy_profiles(self) -> None:
|
104
107
|
"""Copy current user directories into a new user profile."""
|
@@ -107,6 +110,7 @@ class UniqueUserProfile:
|
|
107
110
|
elif "lin" in sys.platform:
|
108
111
|
locs = [".mw/Application Data/Ansys", ".config/Ansys"]
|
109
112
|
for loc in locs:
|
110
|
-
|
111
|
-
|
112
|
-
)
|
113
|
+
default_profile_loc = self._default_profile / loc
|
114
|
+
temp_appdata_loc = self.location / loc
|
115
|
+
if default_profile_loc.exists():
|
116
|
+
shutil.copytree(default_profile_loc, temp_appdata_loc)
|
@@ -26,6 +26,11 @@ A useful subset of what is imported by
|
|
26
26
|
Ansys Inc/v{NNN}/ACT/apis/Mechanical.py
|
27
27
|
"""
|
28
28
|
|
29
|
+
from ansys.mechanical.core.embedding.app import is_initialized
|
30
|
+
|
31
|
+
if not is_initialized():
|
32
|
+
raise Exception("Enums cannot be imported until the embedded app is initialized.")
|
33
|
+
|
29
34
|
import clr
|
30
35
|
|
31
36
|
clr.AddReference("Ansys.Mechanical.DataModel")
|
@@ -0,0 +1,50 @@
|
|
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
|
+
"""Import Mechanical globals."""
|
23
|
+
|
24
|
+
from ansys.mechanical.core.embedding.app import is_initialized
|
25
|
+
|
26
|
+
if not is_initialized():
|
27
|
+
raise Exception("Globals cannot be imported until the embedded app is initialized.")
|
28
|
+
|
29
|
+
import clr
|
30
|
+
|
31
|
+
clr.AddReference("Ansys.Mechanical.DataModel")
|
32
|
+
clr.AddReference("Ansys.ACT.Interfaces")
|
33
|
+
|
34
|
+
|
35
|
+
clr.AddReference("System.Collections")
|
36
|
+
clr.AddReference("Ansys.ACT.WB1")
|
37
|
+
clr.AddReference("Ansys.Mechanical.DataModel")
|
38
|
+
|
39
|
+
# from Ansys.ACT.Mechanical import Transaction
|
40
|
+
# When ansys-pythonnet issue #14 is fixed, uncomment above
|
41
|
+
from Ansys.ACT.Core.Math import Point2D, Point3D # noqa isort: skip
|
42
|
+
from Ansys.ACT.Math import Vector3D # noqa isort: skip
|
43
|
+
from Ansys.Core.Units import Quantity # noqa isort: skip
|
44
|
+
from Ansys.Mechanical.DataModel import MechanicalEnums # noqa isort: skip
|
45
|
+
from Ansys.Mechanical.Graphics import Point, SectionPlane # noqa isort: skip
|
46
|
+
|
47
|
+
from ansys.mechanical.core.embedding.transaction import Transaction # noqa isort: skip
|
48
|
+
|
49
|
+
import System # noqa isort: skip
|
50
|
+
import Ansys # noqa isort: skip
|
@@ -82,7 +82,7 @@ def to_plotter(app: "ansys.mechanical.core.embedding.App"):
|
|
82
82
|
if np_coordinates is None or np_indices is None:
|
83
83
|
continue
|
84
84
|
pv_transform = _transform_to_pyvista(scenegraph_node.Transform)
|
85
|
-
polydata = pv.PolyData(np_coordinates, np_indices).transform(pv_transform)
|
85
|
+
polydata = pv.PolyData(np_coordinates, np_indices).transform(pv_transform, inplace=True)
|
86
86
|
color = pv.Color(bgr_to_rgb_tuple(body.Color))
|
87
87
|
plotter.plot(polydata, color=color, smooth_shading=True)
|
88
88
|
return plotter
|
@@ -46,36 +46,37 @@ def global_variables(app: "ansys.mechanical.core.App", enums: bool = False) -> t
|
|
46
46
|
To also import all the enums, set the parameter enums to true.
|
47
47
|
"""
|
48
48
|
vars = global_entry_points(app)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
clr.AddReference("Ansys.Mechanical.DataModel")
|
54
|
-
# from Ansys.ACT.Mechanical import Transaction
|
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
|
59
|
-
from Ansys.Core.Units import Quantity
|
60
|
-
from Ansys.Mechanical.DataModel import MechanicalEnums
|
61
|
-
from Ansys.Mechanical.Graphics import Point, SectionPlane
|
62
|
-
|
63
|
-
import System # isort: skip
|
64
|
-
import Ansys # isort: skip
|
65
|
-
|
66
|
-
vars["Quantity"] = Quantity
|
67
|
-
vars["System"] = System
|
68
|
-
vars["Ansys"] = Ansys
|
49
|
+
|
50
|
+
from ansys.mechanical.core.embedding.app import is_initialized
|
51
|
+
from ansys.mechanical.core.embedding.transaction import Transaction
|
52
|
+
|
69
53
|
vars["Transaction"] = Transaction
|
70
|
-
|
71
|
-
#
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
54
|
+
|
55
|
+
# Import modules if the app is initialized
|
56
|
+
if is_initialized():
|
57
|
+
from ansys.mechanical.core.embedding.global_importer import (
|
58
|
+
Ansys,
|
59
|
+
MechanicalEnums,
|
60
|
+
Point,
|
61
|
+
Point2D,
|
62
|
+
Point3D,
|
63
|
+
Quantity,
|
64
|
+
SectionPlane,
|
65
|
+
System,
|
66
|
+
Vector3D,
|
67
|
+
)
|
68
|
+
|
69
|
+
vars["Quantity"] = Quantity
|
70
|
+
vars["System"] = System
|
71
|
+
vars["Ansys"] = Ansys
|
72
|
+
vars["MechanicalEnums"] = MechanicalEnums
|
73
|
+
# Graphics
|
74
|
+
vars["Point"] = Point
|
75
|
+
vars["SectionPlane"] = SectionPlane
|
76
|
+
# Math
|
77
|
+
vars["Point2D"] = Point2D
|
78
|
+
vars["Point3D"] = Point3D
|
79
|
+
vars["Vector3D"] = Vector3D
|
79
80
|
|
80
81
|
if enums:
|
81
82
|
vars.update(get_all_enums())
|
@@ -95,32 +96,3 @@ def get_all_enums() -> typing.Dict[str, typing.Any]:
|
|
95
96
|
if type(the_enum).__name__ == "CLRMetatype":
|
96
97
|
enums[attr] = the_enum
|
97
98
|
return enums
|
98
|
-
|
99
|
-
|
100
|
-
class Transaction: # When ansys-pythonnet issue #14 is fixed, this class will be removed
|
101
|
-
"""
|
102
|
-
A class to speed up bulk user interactions using Ansys ACT Mechanical Transaction.
|
103
|
-
|
104
|
-
Example
|
105
|
-
-------
|
106
|
-
>>> with Transaction() as transaction:
|
107
|
-
... pass # Perform bulk user interactions here
|
108
|
-
...
|
109
|
-
"""
|
110
|
-
|
111
|
-
def __init__(self):
|
112
|
-
"""Initialize the Transaction class."""
|
113
|
-
import clr
|
114
|
-
|
115
|
-
clr.AddReference("Ansys.ACT.WB1")
|
116
|
-
import Ansys
|
117
|
-
|
118
|
-
self._transaction = Ansys.ACT.Mechanical.Transaction()
|
119
|
-
|
120
|
-
def __enter__(self):
|
121
|
-
"""Enter the context of the transaction."""
|
122
|
-
return self
|
123
|
-
|
124
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
125
|
-
"""Exit the context of the transaction and disposes of resources."""
|
126
|
-
self._transaction.Dispose()
|
@@ -30,6 +30,7 @@ import warnings
|
|
30
30
|
|
31
31
|
from ansys.mechanical.core.embedding.loader import load_clr
|
32
32
|
from ansys.mechanical.core.embedding.resolver import resolve
|
33
|
+
from ansys.mechanical.core.mechanical import LOG
|
33
34
|
|
34
35
|
INITIALIZED_VERSION = None
|
35
36
|
"""Constant for the initialized version."""
|
@@ -85,7 +86,6 @@ def _get_latest_default_version() -> int:
|
|
85
86
|
If multiple versions are detected, select the latest one, as no specific version is provided.
|
86
87
|
"""
|
87
88
|
awp_roots = [value for key, value in os.environ.items() if key.startswith("AWP_ROOT")]
|
88
|
-
|
89
89
|
if not awp_roots:
|
90
90
|
raise Exception("No Mechanical installations found.")
|
91
91
|
|
@@ -94,13 +94,15 @@ def _get_latest_default_version() -> int:
|
|
94
94
|
folder = os.path.basename(os.path.normpath(path))
|
95
95
|
version = folder.split("v")[-1]
|
96
96
|
versions_found.append(int(version))
|
97
|
+
|
98
|
+
LOG.info(f"Available versions of Mechanical: {versions_found}")
|
99
|
+
|
97
100
|
latest_version = max(versions_found)
|
98
101
|
|
99
102
|
if len(awp_roots) > 1:
|
100
|
-
|
103
|
+
LOG.warning(
|
101
104
|
f"Multiple versions of Mechanical found! Using latest version {latest_version} ..."
|
102
105
|
)
|
103
|
-
|
104
106
|
return latest_version
|
105
107
|
|
106
108
|
|
@@ -110,6 +112,74 @@ def __check_python_interpreter_architecture() -> None:
|
|
110
112
|
raise Exception("Mechanical Embedding requires a 64-bit Python environment.")
|
111
113
|
|
112
114
|
|
115
|
+
def __windows_store_workaround(version: int) -> None:
|
116
|
+
"""Workaround for Windows store.
|
117
|
+
|
118
|
+
See https://github.com/ansys/pymechanical/issues/1136
|
119
|
+
|
120
|
+
Windows store Python uses the Win32 API SetDefaultDllDirectories
|
121
|
+
so that the PATH environment variable isn't scanned for any DLL
|
122
|
+
dependency.
|
123
|
+
|
124
|
+
PyMechanical loads the embedding library which automatically sets
|
125
|
+
these Paths, but this uses the PATH environment variable which doesn't
|
126
|
+
work for Windows store Python.
|
127
|
+
|
128
|
+
We provide a workaround for versions 2024R2 and 2025R1 that sets
|
129
|
+
these paths using `os.add_dll_directory`.
|
130
|
+
|
131
|
+
Note: This workaround does not include DLL paths used in FSI
|
132
|
+
mapping workflows.
|
133
|
+
|
134
|
+
Parameters
|
135
|
+
----------
|
136
|
+
version : int
|
137
|
+
The version of Mechanical to set the DLL paths for.
|
138
|
+
"""
|
139
|
+
# Nothing to do on Linux
|
140
|
+
if os.name != "nt":
|
141
|
+
return
|
142
|
+
|
143
|
+
# Nothing to do if it isn't a Windows store application
|
144
|
+
if r"Microsoft\WindowsApps" not in sys.executable:
|
145
|
+
return
|
146
|
+
|
147
|
+
# Get the AWP_ROOT environment variable for the specified version
|
148
|
+
awp_root = Path(os.environ[f"AWP_ROOT{version}"])
|
149
|
+
# Set paths to the aisol and framework DLLs
|
150
|
+
paths = [
|
151
|
+
awp_root / "aisol" / "bin" / "winx64",
|
152
|
+
awp_root / "Framework" / "bin" / "Win64",
|
153
|
+
]
|
154
|
+
# Set the path to the tp directory within the AWP_ROOTXYZ directory
|
155
|
+
awp_root_tp = awp_root / "tp"
|
156
|
+
# Add paths to the IntelCompiler, IntelMKL, HDF5, and Qt DLLs for 2024R2 and 2025R1
|
157
|
+
if version == 242:
|
158
|
+
paths.extend(
|
159
|
+
[
|
160
|
+
awp_root_tp / "IntelCompiler" / "2023.1.0" / "winx64",
|
161
|
+
awp_root_tp / "IntelMKL" / "2023.1.0" / "winx64",
|
162
|
+
awp_root_tp / "hdf5" / "1.12.2" / "winx64",
|
163
|
+
awp_root_tp / "qt" / "5.15.16" / "winx64" / "bin",
|
164
|
+
]
|
165
|
+
)
|
166
|
+
elif version == 251:
|
167
|
+
paths.extend(
|
168
|
+
[
|
169
|
+
awp_root_tp / "IntelCompiler" / "2023.1.0" / "winx64",
|
170
|
+
awp_root_tp / "IntelMKL" / "2023.1.0" / "winx64",
|
171
|
+
awp_root_tp / "hdf5" / "1.12.2" / "winx64",
|
172
|
+
awp_root_tp / "qt" / "5.15.17" / "winx64" / "bin",
|
173
|
+
]
|
174
|
+
)
|
175
|
+
else:
|
176
|
+
return
|
177
|
+
|
178
|
+
# Add each path to the DLL search path
|
179
|
+
for path in paths:
|
180
|
+
os.add_dll_directory(str(path))
|
181
|
+
|
182
|
+
|
113
183
|
def __set_environment(version: int) -> None:
|
114
184
|
"""Set environment variables to configure embedding."""
|
115
185
|
if os.name == "nt": # pragma: no cover
|
@@ -159,7 +229,7 @@ def __check_loaded_libs(version: int = None): # pragma: no cover
|
|
159
229
|
# For 2025 R1, PyMechanical will crash on shutdown if libX11.so is already loaded
|
160
230
|
# before starting Mechanical
|
161
231
|
if __is_lib_loaded("libX11.so"):
|
162
|
-
|
232
|
+
LOG.warning(
|
163
233
|
"libX11.so is loaded prior to initializing the Embedded Instance of Mechanical.\
|
164
234
|
Python will crash on shutdown..."
|
165
235
|
)
|
@@ -186,6 +256,8 @@ def initialize(version: int = None):
|
|
186
256
|
|
187
257
|
__set_environment(version)
|
188
258
|
|
259
|
+
__windows_store_workaround(version)
|
260
|
+
|
189
261
|
__check_loaded_libs(version)
|
190
262
|
|
191
263
|
__workaround_material_server(version)
|