ansys-mechanical-core 0.11.7__tar.gz → 0.11.9__tar.gz

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 (44) hide show
  1. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/PKG-INFO +22 -19
  2. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/pyproject.toml +25 -22
  3. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/addins.py +0 -6
  4. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/app.py +65 -8
  5. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/background.py +5 -5
  6. ansys_mechanical_core-0.11.9/src/ansys/mechanical/core/embedding/cleanup_gui.py +61 -0
  7. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/initializer.py +91 -34
  8. ansys_mechanical_core-0.11.9/src/ansys/mechanical/core/embedding/ui.py +228 -0
  9. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/viz/utils.py +2 -1
  10. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/examples/downloads.py +9 -4
  11. ansys_mechanical_core-0.11.9/src/ansys/mechanical/core/ide_config.py +212 -0
  12. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/launcher.py +8 -8
  13. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/mechanical.py +2 -3
  14. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/pool.py +5 -5
  15. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/run.py +5 -4
  16. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/LICENSE +0 -0
  17. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/README.rst +0 -0
  18. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/__init__.py +0 -0
  19. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/_version.py +0 -0
  20. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/__init__.py +0 -0
  21. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/app_libraries.py +0 -0
  22. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/appdata.py +0 -0
  23. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/enum_importer.py +0 -0
  24. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/imports.py +0 -0
  25. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/loader.py +0 -0
  26. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/logger/__init__.py +0 -0
  27. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/logger/environ.py +0 -0
  28. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/logger/linux_api.py +0 -0
  29. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/logger/sinks.py +0 -0
  30. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/logger/windows_api.py +0 -0
  31. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/poster.py +0 -0
  32. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/resolver.py +0 -0
  33. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/runtime.py +0 -0
  34. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/shims.py +0 -0
  35. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/utils.py +0 -0
  36. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/viz/__init__.py +0 -0
  37. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/viz/embedding_plotter.py +0 -0
  38. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/viz/usd_converter.py +0 -0
  39. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/embedding/warnings.py +0 -0
  40. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/errors.py +0 -0
  41. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/examples/__init__.py +0 -0
  42. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/feature_flags.py +0 -0
  43. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/logging.py +0 -0
  44. {ansys_mechanical_core-0.11.7 → ansys_mechanical_core-0.11.9}/src/ansys/mechanical/core/misc.py +0 -0
@@ -1,23 +1,23 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ansys-mechanical-core
3
- Version: 0.11.7
3
+ Version: 0.11.9
4
4
  Summary: A python wrapper for Ansys Mechanical
5
5
  Keywords: pymechanical,mechanical,ansys,pyansys
6
6
  Author-email: "ANSYS, Inc." <pyansys.core@ansys.com>
7
7
  Maintainer-email: "ANSYS, Inc." <pyansys.core@ansys.com>
8
- Requires-Python: >=3.9,<4.0
8
+ Requires-Python: >=3.10,<4.0
9
9
  Description-Content-Type: text/x-rst
10
10
  Classifier: Development Status :: 4 - Beta
11
11
  Classifier: Intended Audience :: Science/Research
12
12
  Classifier: Topic :: Scientific/Engineering :: Information Analysis
13
- Classifier: Programming Language :: Python :: 3.9
14
13
  Classifier: Programming Language :: Python :: 3.10
15
14
  Classifier: Programming Language :: Python :: 3.11
16
15
  Classifier: Programming Language :: Python :: 3.12
17
16
  Classifier: License :: OSI Approved :: MIT License
18
17
  Classifier: Operating System :: OS Independent
19
18
  Requires-Dist: ansys-api-mechanical==0.1.2
20
- Requires-Dist: ansys-mechanical-env==0.1.7
19
+ Requires-Dist: ansys-mechanical-env==0.1.8
20
+ Requires-Dist: ansys-mechanical-stubs==0.1.4
21
21
  Requires-Dist: ansys-platform-instancemanagement>=1.0.1
22
22
  Requires-Dist: ansys-pythonnet>=3.1.0rc2
23
23
  Requires-Dist: ansys-tools-path>=0.3.1
@@ -26,37 +26,40 @@ Requires-Dist: click>=8.1.3
26
26
  Requires-Dist: clr-loader==0.2.6
27
27
  Requires-Dist: grpcio>=1.30.0
28
28
  Requires-Dist: protobuf>=3.12.2,<6
29
+ Requires-Dist: psutil==6.1.0
29
30
  Requires-Dist: tqdm>=4.45.0
30
- Requires-Dist: sphinx==8.0.2 ; extra == "doc"
31
- Requires-Dist: ansys-sphinx-theme[autoapi]==1.0.7 ; extra == "doc"
32
- Requires-Dist: grpcio==1.66.0 ; extra == "doc"
31
+ Requires-Dist: requests>=2,<3
32
+ Requires-Dist: sphinx==8.1.3 ; extra == "doc"
33
+ Requires-Dist: ansys-sphinx-theme[autoapi]==1.1.7 ; extra == "doc"
34
+ Requires-Dist: grpcio==1.67.0 ; extra == "doc"
33
35
  Requires-Dist: imageio-ffmpeg==0.5.1 ; extra == "doc"
34
- Requires-Dist: imageio==2.35.1 ; extra == "doc"
36
+ Requires-Dist: imageio==2.36.0 ; extra == "doc"
35
37
  Requires-Dist: jupyter_sphinx==0.5.3 ; extra == "doc"
36
38
  Requires-Dist: jupyterlab>=3.2.8 ; extra == "doc"
37
39
  Requires-Dist: matplotlib==3.9.2 ; extra == "doc"
38
- Requires-Dist: numpy==2.1.0 ; extra == "doc"
40
+ Requires-Dist: numpy==2.1.2 ; extra == "doc"
39
41
  Requires-Dist: numpydoc==1.8.0 ; extra == "doc"
40
- Requires-Dist: pandas==2.2.2 ; extra == "doc"
41
- Requires-Dist: panel==1.4.5 ; extra == "doc"
42
- Requires-Dist: plotly==5.23.0 ; extra == "doc"
43
- Requires-Dist: pypandoc==1.13 ; extra == "doc"
42
+ Requires-Dist: pandas==2.2.3 ; extra == "doc"
43
+ Requires-Dist: panel==1.5.3 ; extra == "doc"
44
+ Requires-Dist: plotly==5.24.1 ; extra == "doc"
45
+ Requires-Dist: pypandoc==1.14 ; extra == "doc"
44
46
  Requires-Dist: pytest-sphinx==0.6.3 ; extra == "doc"
45
47
  Requires-Dist: pythreejs==2.4.2 ; extra == "doc"
46
48
  Requires-Dist: pyvista>=0.39.1 ; extra == "doc"
47
- Requires-Dist: sphinx-autobuild==2024.4.16 ; extra == "doc"
48
- Requires-Dist: sphinx-autodoc-typehints==2.2.3 ; extra == "doc"
49
+ Requires-Dist: sphinx-autobuild==2024.10.3 ; extra == "doc"
50
+ Requires-Dist: sphinx-autodoc-typehints==2.5.0 ; extra == "doc"
49
51
  Requires-Dist: sphinx-copybutton==0.5.2 ; extra == "doc"
50
52
  Requires-Dist: sphinx_design==0.6.1 ; extra == "doc"
51
- Requires-Dist: sphinx-gallery==0.17.1 ; extra == "doc"
53
+ Requires-Dist: sphinx-gallery==0.18.0 ; extra == "doc"
52
54
  Requires-Dist: sphinx-notfound-page==1.0.4 ; extra == "doc"
53
55
  Requires-Dist: sphinxcontrib-websupport==2.0.0 ; extra == "doc"
54
56
  Requires-Dist: sphinxemoji==0.3.1 ; extra == "doc"
55
- Requires-Dist: pytest==8.3.2 ; extra == "tests"
57
+ Requires-Dist: pytest==8.3.3 ; extra == "tests"
56
58
  Requires-Dist: pytest-cov==5.0.0 ; extra == "tests"
57
- Requires-Dist: pytest-print==1.0.0 ; extra == "tests"
59
+ Requires-Dist: pytest-print==1.0.2 ; extra == "tests"
60
+ Requires-Dist: psutil==6.1.0 ; extra == "tests"
58
61
  Requires-Dist: ansys-tools-visualization-interface>=0.2.6 ; extra == "viz"
59
- Requires-Dist: usd-core==24.8 ; extra == "viz"
62
+ Requires-Dist: usd-core==24.11 ; extra == "viz"
60
63
  Project-URL: Changelog, https://mechanical.docs.pyansys.com/version/stable/changelog.html
61
64
  Project-URL: Documentation, https://mechanical.docs.pyansys.com
62
65
  Project-URL: Homepage, https://github.com/ansys/pymechanical
@@ -5,10 +5,10 @@ build-backend = "flit_core.buildapi"
5
5
  [project]
6
6
  # Check https://flit.readthedocs.io/en/latest/pyproject_toml.html for all available sections
7
7
  name = "ansys-mechanical-core"
8
- version = "0.11.7"
8
+ version = "0.11.9"
9
9
  description = "A python wrapper for Ansys Mechanical"
10
10
  readme = "README.rst"
11
- requires-python = ">=3.9,<4.0"
11
+ requires-python = ">=3.10,<4.0"
12
12
  license = {file = "LICENSE"}
13
13
  authors = [{name = "ANSYS, Inc.", email = "pyansys.core@ansys.com"}]
14
14
  maintainers = [{name = "ANSYS, Inc.", email = "pyansys.core@ansys.com"}]
@@ -18,7 +18,6 @@ classifiers = [
18
18
  "Development Status :: 4 - Beta",
19
19
  'Intended Audience :: Science/Research',
20
20
  'Topic :: Scientific/Engineering :: Information Analysis',
21
- "Programming Language :: Python :: 3.9",
22
21
  "Programming Language :: Python :: 3.10",
23
22
  "Programming Language :: Python :: 3.11",
24
23
  "Programming Language :: Python :: 3.12",
@@ -27,7 +26,8 @@ classifiers = [
27
26
  ]
28
27
  dependencies = [
29
28
  "ansys-api-mechanical==0.1.2",
30
- "ansys-mechanical-env==0.1.7",
29
+ "ansys-mechanical-env==0.1.8",
30
+ "ansys-mechanical-stubs==0.1.4",
31
31
  "ansys-platform-instancemanagement>=1.0.1",
32
32
  "ansys-pythonnet>=3.1.0rc2",
33
33
  "ansys-tools-path>=0.3.1",
@@ -36,7 +36,9 @@ dependencies = [
36
36
  "clr-loader==0.2.6",
37
37
  "grpcio>=1.30.0",
38
38
  "protobuf>=3.12.2,<6",
39
+ "psutil==6.1.0",
39
40
  "tqdm>=4.45.0",
41
+ "requests>=2,<3",
40
42
  ]
41
43
 
42
44
  [project.urls]
@@ -48,44 +50,46 @@ Changelog = "https://mechanical.docs.pyansys.com/version/stable/changelog.html"
48
50
 
49
51
  [project.optional-dependencies]
50
52
  tests = [
51
- "pytest==8.3.2",
53
+ "pytest==8.3.3",
52
54
  "pytest-cov==5.0.0",
53
- "pytest-print==1.0.0",
55
+ "pytest-print==1.0.2",
56
+ "psutil==6.1.0"
54
57
  ]
55
58
  doc = [
56
- "sphinx==8.0.2",
57
- "ansys-sphinx-theme[autoapi]==1.0.7",
58
- "grpcio==1.66.0",
59
+ "sphinx==8.1.3",
60
+ "ansys-sphinx-theme[autoapi]==1.1.7",
61
+ "grpcio==1.67.0",
59
62
  "imageio-ffmpeg==0.5.1",
60
- "imageio==2.35.1",
63
+ "imageio==2.36.0",
61
64
  "jupyter_sphinx==0.5.3",
62
65
  "jupyterlab>=3.2.8",
63
66
  "matplotlib==3.9.2",
64
- "numpy==2.1.0",
67
+ "numpy==2.1.2",
65
68
  "numpydoc==1.8.0",
66
- "pandas==2.2.2",
67
- "panel==1.4.5",
68
- "plotly==5.23.0",
69
- "pypandoc==1.13",
69
+ "pandas==2.2.3",
70
+ "panel==1.5.3",
71
+ "plotly==5.24.1",
72
+ "pypandoc==1.14",
70
73
  "pytest-sphinx==0.6.3",
71
74
  "pythreejs==2.4.2",
72
75
  "pyvista>=0.39.1",
73
- "sphinx-autobuild==2024.4.16",
74
- "sphinx-autodoc-typehints==2.2.3",
76
+ "sphinx-autobuild==2024.10.3",
77
+ "sphinx-autodoc-typehints==2.5.0",
75
78
  "sphinx-copybutton==0.5.2",
76
79
  "sphinx_design==0.6.1",
77
- "sphinx-gallery==0.17.1",
80
+ "sphinx-gallery==0.18.0",
78
81
  "sphinx-notfound-page==1.0.4",
79
82
  "sphinxcontrib-websupport==2.0.0",
80
83
  "sphinxemoji==0.3.1",
81
84
  ]
82
85
  viz = [
83
86
  "ansys-tools-visualization-interface>=0.2.6",
84
- "usd-core==24.8",
87
+ "usd-core==24.11",
85
88
  ]
86
89
 
87
90
  [project.scripts]
88
91
  ansys-mechanical = "ansys.mechanical.core.run:cli"
92
+ ansys-mechanical-ideconfig = "ansys.mechanical.core.ide_config:cli"
89
93
 
90
94
  [tool.flit.module]
91
95
  name = "ansys.mechanical.core"
@@ -138,6 +142,7 @@ markers = [
138
142
  "remote_session_launch: tests that launch Mechanical and work with gRPC server inside it",
139
143
  "remote_session_connect: tests that connect to Mechanical and work with gRPC server inside it",
140
144
  "minimum_version(num): tests that run if ansys-version is greater than or equal to the minimum version provided",
145
+ "version_range(min_revn,max_revn): tests that run if ansys-version is in the range of the minimum and maximum revision numbers inclusive.",
141
146
  "windows_only: tests that run if the testing platform is on Windows",
142
147
  "linux_only: tests that run if the testing platform is on Linux",
143
148
  "cli: tests for the Command Line Interface",
@@ -207,7 +212,7 @@ legacy_tox_ini = """
207
212
  [tox]
208
213
  description = Default tox environments list
209
214
  envlist =
210
- style,{py39,py310,py311,py312}{,-coverage},doc
215
+ style,{py310,py311,py312}{,-coverage},doc
211
216
  passenv = AWP_ROOT
212
217
  skip_missing_interpreters = true
213
218
  isolated_build = true
@@ -216,7 +221,6 @@ isolated_build_env = build
216
221
  [gh-actions]
217
222
  description = The tox environment to be executed in gh-actions for a given python version
218
223
  python =
219
- 3.9: style,py39-coverage,doc
220
224
  3.10: style,py310-coverage,doc
221
225
  3.11: style,py311-coverage,doc
222
226
  3.12: style,py311-coverage,doc
@@ -224,7 +228,6 @@ python =
224
228
  [testenv]
225
229
  description = Checks for project unit tests and coverage (if desired)
226
230
  basepython =
227
- py39: python3.9
228
231
  py310: python3.10
229
232
  py311: python3.11
230
233
  py312: python3.12
@@ -52,9 +52,3 @@ class AddinConfiguration:
52
52
  @addin_configuration.setter
53
53
  def addin_configuration(self, value: str):
54
54
  self._addin_configuration = value
55
-
56
-
57
- def configure(configuration: AddinConfiguration):
58
- """Apply the given configuration."""
59
- if configuration.no_act_addins:
60
- os.environ["ANSYS_MECHANICAL_STANDALONE_NO_ACT_EXTENSIONS"] = "1"
@@ -21,8 +21,11 @@
21
21
  # SOFTWARE.
22
22
 
23
23
  """Main application class for embedded Mechanical."""
24
+ from __future__ import annotations
25
+
24
26
  import atexit
25
27
  import os
28
+ import shutil
26
29
  import typing
27
30
  import warnings
28
31
 
@@ -31,15 +34,20 @@ from ansys.mechanical.core.embedding.addins import AddinConfiguration
31
34
  from ansys.mechanical.core.embedding.appdata import UniqueUserProfile
32
35
  from ansys.mechanical.core.embedding.imports import global_entry_points, global_variables
33
36
  from ansys.mechanical.core.embedding.poster import Poster
37
+ from ansys.mechanical.core.embedding.ui import launch_ui
34
38
  from ansys.mechanical.core.embedding.warnings import connect_warnings, disconnect_warnings
35
39
 
40
+ if typing.TYPE_CHECKING:
41
+ # Make sure to run ``ansys-mechanical-ideconfig`` to add the autocomplete settings to VS Code
42
+ # Run ``ansys-mechanical-ideconfig --help`` for more information
43
+ import Ansys # pragma: no cover
44
+
36
45
  try:
37
46
  import ansys.tools.visualization_interface # noqa: F401
38
47
 
39
48
  HAS_ANSYS_VIZ = True
40
49
  """Whether or not PyVista exists."""
41
50
  except:
42
-
43
51
  HAS_ANSYS_VIZ = False
44
52
 
45
53
 
@@ -187,10 +195,45 @@ class App:
187
195
  else:
188
196
  self.DataModel.Project.Save()
189
197
 
190
- def save_as(self, path):
191
- """Save the project as."""
198
+ def save_as(self, path: str, overwrite: bool = False):
199
+ """Save the project as a new file.
200
+
201
+ If the `overwrite` flag is enabled, the current saved file is temporarily moved
202
+ to a backup location. The new file is then saved in its place. If the process fails,
203
+ the backup file is restored to its original location.
204
+
205
+ Parameters
206
+ ----------
207
+ path: int, optional
208
+ The path where file needs to be saved.
209
+ overwrite: bool, optional
210
+ Whether the file should be overwritten if it already exists (default is False).
211
+ """
212
+ if not os.path.exists(path):
213
+ self.DataModel.Project.SaveAs(path)
214
+ return
215
+
216
+ if not overwrite:
217
+ raise Exception(
218
+ f"File already exists in {path}, Use ``overwrite`` flag to "
219
+ "replace the existing file."
220
+ )
221
+
222
+ file_name = os.path.basename(path)
223
+ file_dir = os.path.dirname(path)
224
+ associated_dir = os.path.join(file_dir, os.path.splitext(file_name)[0] + "_Mech_Files")
225
+
226
+ # Remove existing files and associated folder
227
+ os.remove(path)
228
+ if os.path.exists(associated_dir):
229
+ shutil.rmtree(associated_dir)
230
+ # Save the new file
192
231
  self.DataModel.Project.SaveAs(path)
193
232
 
233
+ def launch_gui(self, delete_tmp_on_close: bool = True, dry_run: bool = False):
234
+ """Launch the GUI."""
235
+ launch_ui(self, delete_tmp_on_close, dry_run)
236
+
194
237
  def new(self):
195
238
  """Clear to a new application."""
196
239
  self.DataModel.Project.New()
@@ -227,7 +270,21 @@ class App:
227
270
  light_mode = True
228
271
  args = None
229
272
  rets = None
230
- return self.script_engine.ExecuteCode(script, SCRIPT_SCOPE, light_mode, args, rets)
273
+ script_result = self.script_engine.ExecuteCode(script, SCRIPT_SCOPE, light_mode, args, rets)
274
+ error_msg = f"Failed to execute the script"
275
+ if script_result is None:
276
+ raise Exception(error_msg)
277
+ if script_result.Error is not None:
278
+ error_msg += f": {script_result.Error.Message}"
279
+ raise Exception(error_msg)
280
+ return script_result.Value
281
+
282
+ def execute_script_from_file(self, file_path=None):
283
+ """Execute the given script from file with the internal IronPython engine."""
284
+ text_file = open(file_path, "r", encoding="utf-8")
285
+ data = text_file.read()
286
+ text_file.close()
287
+ return self.execute_script(data)
231
288
 
232
289
  def plotter(self) -> None:
233
290
  """Return ``ansys.tools.visualization_interface.Plotter`` object."""
@@ -273,22 +330,22 @@ class App:
273
330
  return GetterWrapper(self._app, lambda app: app.DataModel)
274
331
 
275
332
  @property
276
- def ExtAPI(self):
333
+ def ExtAPI(self) -> Ansys.ACT.Interfaces.Mechanical.IMechanicalExtAPI:
277
334
  """Return the ExtAPI object."""
278
335
  return GetterWrapper(self._app, lambda app: app.ExtAPI)
279
336
 
280
337
  @property
281
- def Tree(self):
338
+ def Tree(self) -> Ansys.ACT.Automation.Mechanical.Tree:
282
339
  """Return the Tree object."""
283
340
  return GetterWrapper(self._app, lambda app: app.DataModel.Tree)
284
341
 
285
342
  @property
286
- def Model(self):
343
+ def Model(self) -> Ansys.ACT.Automation.Mechanical.Model:
287
344
  """Return the Model object."""
288
345
  return GetterWrapper(self._app, lambda app: app.DataModel.Project.Model)
289
346
 
290
347
  @property
291
- def Graphics(self):
348
+ def Graphics(self) -> Ansys.ACT.Common.Graphics.MechanicalGraphicsWrapper:
292
349
  """Return the Graphics object."""
293
350
  return GetterWrapper(self._app, lambda app: app.ExtAPI.Graphics)
294
351
 
@@ -59,9 +59,8 @@ class BackgroundApp:
59
59
  time.sleep(0.05)
60
60
  continue
61
61
  else:
62
- assert (
63
- not BackgroundApp.__stopped
64
- ), "Cannot initialize a BackgroundApp once it has been stopped!"
62
+ if BackgroundApp.__stopped:
63
+ raise RuntimeError("Cannot initialize a BackgroundApp once it has been stopped!")
65
64
 
66
65
  def new():
67
66
  BackgroundApp.__app.new()
@@ -80,7 +79,8 @@ class BackgroundApp:
80
79
 
81
80
  def post(self, callable: typing.Callable):
82
81
  """Post callable method to the background app thread."""
83
- assert not BackgroundApp.__stopped, "Cannot use background app after stopping it."
82
+ if BackgroundApp.__stopped:
83
+ raise RuntimeError("Cannot use BackgroundApp after stopping it.")
84
84
  return BackgroundApp.__poster.post(callable)
85
85
 
86
86
  def stop(self) -> None:
@@ -102,5 +102,5 @@ class BackgroundApp:
102
102
  try:
103
103
  utils.sleep(40)
104
104
  except:
105
- pass
105
+ raise Exception("BackgroundApp cannot sleep.") # pragma: no cover
106
106
  BackgroundApp.__stopped = True
@@ -0,0 +1,61 @@
1
+ # Copyright (C) 2022 - 2024 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)
@@ -28,15 +28,13 @@ 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."""
38
36
 
39
- SUPPORTED_MECHANICAL_EMBEDDING_VERSIONS_WINDOWS = {242: "2024R2", 241: "2024R1", 232: "2023R2"}
37
+ SUPPORTED_MECHANICAL_EMBEDDING_VERSIONS = {242: "2024R2", 241: "2024R1", 232: "2023R2"}
40
38
  """Supported Mechanical embedding versions on Windows."""
41
39
 
42
40
 
@@ -59,48 +57,68 @@ def __workaround_material_server(version: int) -> None:
59
57
  os.environ["ENGRDATA_SERVER_SERIAL"] = "1"
60
58
 
61
59
 
62
- def _get_default_linux_version() -> int:
63
- """Try to get the active linux version from the environment.
60
+ def __check_for_supported_version(version) -> None:
61
+ """Check if Mechanical version is supported with current version of PyMechanical.
64
62
 
65
- On linux, embedding is only possible by setting environment variables before starting python.
66
- The version will then be fixed to a specific version, based on those env vars.
67
- The documented way to set those variables is to run python using the ``mechanical-env`` script,
68
- which can be used after installing the ``ansys-mechanical-env`` package with this command:
69
- ``pip install ansys-mechanical-env``. The script takes user input of a version. If the user
70
- does not provide a version, the ``find_mechanical()`` function from the ``ansys-tools-path``
71
- package is used to find a version of Mechanical.
63
+ If specific environment variable is enabled, then users can overwrite the supported versions.
64
+ However, using unsupported versions may cause issues.
72
65
  """
73
- supported_versions = [232, 241, 242]
74
- awp_roots = {ver: os.environ.get(f"AWP_ROOT{ver}", "") for ver in supported_versions}
75
- installed_versions = {
76
- ver: path for ver, path in awp_roots.items() if path and os.path.isdir(path)
77
- }
78
- assert len(installed_versions) == 1, "multiple AWP_ROOT environment variables found!"
79
- return next(iter(installed_versions))
66
+ allow_old_version = os.getenv("ANSYS_MECHANICAL_EMBEDDING_SUPPORT_OLD_VERSIONS") == "1"
67
+
68
+ # Check if the version is supported
69
+ if not allow_old_version and version < min(SUPPORTED_MECHANICAL_EMBEDDING_VERSIONS):
70
+ raise ValueError(f"Mechanical version {version} is not supported.")
71
+
72
+ return version
80
73
 
81
74
 
82
- def _get_default_version() -> int:
83
- if os.name == "posix":
84
- return _get_default_linux_version()
75
+ def _get_latest_default_version() -> int:
76
+ """Try to get the latest Mechanical version from the environment.
77
+
78
+ Checks if multiple versions of Mechanical found in system.
79
+ For Linux it will be only one since ``mechanical-env`` takes care of that.
80
+ If multiple versions are detected, select the latest one, as no specific version is provided.
81
+ """
82
+ awp_roots = [value for key, value in os.environ.items() if key.startswith("AWP_ROOT")]
83
+
84
+ if not awp_roots:
85
+ raise Exception("No Mechanical installations found.")
85
86
 
86
- if os.name != "nt": # pragma: no cover
87
- raise Exception("Unexpected platform!")
87
+ versions_found = []
88
+ for path in awp_roots:
89
+ folder = os.path.basename(os.path.normpath(path))
90
+ version = folder.split("v")[-1]
91
+ versions_found.append(int(version))
92
+ latest_version = max(versions_found)
88
93
 
89
- _, version = atp.find_mechanical(
90
- supported_versions=SUPPORTED_MECHANICAL_EMBEDDING_VERSIONS_WINDOWS
91
- )
94
+ if len(awp_roots) > 1:
95
+ warnings.warn(
96
+ f"Multiple versions of Mechanical found! Using latest version {latest_version} ..."
97
+ )
92
98
 
93
- # version is of the form 23.2
94
- int_version = int(str(version).replace(".", ""))
95
- return int_version
99
+ return latest_version
96
100
 
97
101
 
98
- def __check_python_interpreter_architecture():
102
+ def __check_python_interpreter_architecture() -> None:
99
103
  """Embedding support only 64 bit architecture."""
100
104
  if platform.architecture()[0] != "64bit":
101
105
  raise Exception("Mechanical Embedding requires a 64-bit Python environment.")
102
106
 
103
107
 
108
+ def __set_environment(version: int) -> None:
109
+ """Set environment variables to configure embedding."""
110
+ if os.name == "nt": # pragma: no cover
111
+ if version < 251:
112
+ os.environ["MECHANICAL_STARTUP_UNOPTIMIZED"] = "1"
113
+
114
+ # Set an environment variable to use the custom CLR host
115
+ # for embedding.
116
+ # In the future (>251), it would always be used.
117
+ if version == 251:
118
+ if "PYMECHANICAL_NO_CLR_HOST_LITE" not in os.environ:
119
+ os.environ["ANSYS_MECHANICAL_EMBEDDING_CLR_HOST"] = "1"
120
+
121
+
104
122
  def __check_for_mechanical_env():
105
123
  """Embedding in linux platform must use mechanical-env."""
106
124
  if platform.system() == "Linux" and os.environ.get("PYMECHANICAL_EMBEDDING") != "TRUE":
@@ -113,21 +131,60 @@ def __check_for_mechanical_env():
113
131
  )
114
132
 
115
133
 
134
+ def __is_lib_loaded(libname: str): # pragma: no cover
135
+ """Return whether a library is loaded."""
136
+ import ctypes
137
+
138
+ RTLD_NOLOAD = 4
139
+ try:
140
+ ctypes.CDLL(libname, RTLD_NOLOAD)
141
+ except:
142
+ return False
143
+ return True
144
+
145
+
146
+ def __check_loaded_libs(version: int = None): # pragma: no cover
147
+ """Ensure that incompatible libraries aren't loaded prior to PyMechanical load."""
148
+ if platform.system() != "Linux":
149
+ return
150
+
151
+ if version < 251:
152
+ return
153
+
154
+ # For 2025 R1, PyMechanical will crash on shutdown if libX11.so is already loaded
155
+ # before starting Mechanical
156
+ if __is_lib_loaded("libX11.so"):
157
+ warnings.warn(
158
+ "libX11.so is loaded prior to initializing the Embedded Instance of Mechanical.\
159
+ Python will crash on shutdown..."
160
+ )
161
+
162
+
116
163
  def initialize(version: int = None):
117
164
  """Initialize Mechanical embedding."""
118
165
  __check_python_interpreter_architecture() # blocks 32 bit python
119
166
  __check_for_mechanical_env() # checks for mechanical-env in linux embedding
120
167
 
121
168
  global INITIALIZED_VERSION
122
- if INITIALIZED_VERSION != None:
123
- assert INITIALIZED_VERSION == version
169
+ if INITIALIZED_VERSION is not None:
170
+ if INITIALIZED_VERSION != version:
171
+ raise ValueError(
172
+ f"Initialized version {INITIALIZED_VERSION} "
173
+ f"does not match the expected version {version}."
174
+ )
124
175
  return
125
176
 
126
177
  if version == None:
127
- version = _get_default_version()
178
+ version = _get_latest_default_version()
179
+
180
+ version = __check_for_supported_version(version=version)
128
181
 
129
182
  INITIALIZED_VERSION = version
130
183
 
184
+ __set_environment(version)
185
+
186
+ __check_loaded_libs(version)
187
+
131
188
  __workaround_material_server(version)
132
189
 
133
190
  # need to add system path in order to import the assembly with the resolver