ansys-workbench-core 0.4.0__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.
- ansys_workbench_core-0.4.0/LICENSE +21 -0
- ansys_workbench_core-0.4.0/PKG-INFO +122 -0
- ansys_workbench_core-0.4.0/README.rst +82 -0
- ansys_workbench_core-0.4.0/pyproject.toml +136 -0
- ansys_workbench_core-0.4.0/src/ansys/workbench/core/__init__.py +32 -0
- ansys_workbench_core-0.4.0/src/ansys/workbench/core/example_data.py +97 -0
- ansys_workbench_core-0.4.0/src/ansys/workbench/core/public_api.py +329 -0
- ansys_workbench_core-0.4.0/src/ansys/workbench/core/py.typed +0 -0
- ansys_workbench_core-0.4.0/src/ansys/workbench/core/workbench_client.py +528 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 - 2024 ANSYS, Inc. and/or its affiliates.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
9
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
10
|
+
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.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: ansys-workbench-core
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: A python wrapper for Ansys Workbench
|
|
5
|
+
Author-email: "ANSYS, Inc." <pyansys.core@ansys.com>
|
|
6
|
+
Maintainer-email: "ANSYS, Inc." <pyansys.core@ansys.com>
|
|
7
|
+
Requires-Python: >=3.9,<4.0
|
|
8
|
+
Description-Content-Type: text/x-rst
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Topic :: Scientific/Engineering
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Requires-Dist: ansys_api_workbench>=0.2.0
|
|
18
|
+
Requires-Dist: ansys-platform-instancemanagement>=1.0.1
|
|
19
|
+
Requires-Dist: ansys-pythonnet>=3.1.0rc1
|
|
20
|
+
Requires-Dist: ansys-tools-path>=0.3.1
|
|
21
|
+
Requires-Dist: tqdm>=4.65.0
|
|
22
|
+
Requires-Dist: WMI>=1.4.9
|
|
23
|
+
Requires-Dist: ansys-sphinx-theme[autoapi]==0.16.5 ; extra == "doc"
|
|
24
|
+
Requires-Dist: numpydoc==1.7.0 ; extra == "doc"
|
|
25
|
+
Requires-Dist: Sphinx==7.3.7 ; extra == "doc"
|
|
26
|
+
Requires-Dist: sphinx-autodoc-typehints==2.1.1 ; extra == "doc"
|
|
27
|
+
Requires-Dist: sphinx-copybutton==0.5.2 ; extra == "doc"
|
|
28
|
+
Requires-Dist: pytest==8.2.2 ; extra == "tests"
|
|
29
|
+
Requires-Dist: pytest-cov==5.0.0 ; extra == "tests"
|
|
30
|
+
Requires-Dist: bandit==1.7.8 ; extra == "vulnerabilities"
|
|
31
|
+
Requires-Dist: safety==3.2.3 ; extra == "vulnerabilities"
|
|
32
|
+
Project-URL: Documentation, https://workbench.docs.pyansys.com
|
|
33
|
+
Project-URL: Homepage, https://github.com/ansys/pyworkbench
|
|
34
|
+
Project-URL: Source, https://github.com/ansys/pyworkbench
|
|
35
|
+
Project-URL: Tracker, https://github.com/ansys/pyworkbench/issues
|
|
36
|
+
Provides-Extra: doc
|
|
37
|
+
Provides-Extra: tests
|
|
38
|
+
Provides-Extra: vulnerabilities
|
|
39
|
+
|
|
40
|
+
PyWorkbench
|
|
41
|
+
===========
|
|
42
|
+
|
|
43
|
+
|pyansys| |python| |pypi| |GH-CI| |MIT| |ruff|
|
|
44
|
+
|
|
45
|
+
.. |pyansys| image:: https://img.shields.io/badge/Py-Ansys-ffc107.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABDklEQVQ4jWNgoDfg5mD8vE7q/3bpVyskbW0sMRUwofHD7Dh5OBkZGBgW7/3W2tZpa2tLQEOyOzeEsfumlK2tbVpaGj4N6jIs1lpsDAwMJ278sveMY2BgCA0NFRISwqkhyQ1q/Nyd3zg4OBgYGNjZ2ePi4rB5loGBhZnhxTLJ/9ulv26Q4uVk1NXV/f///////69du4Zdg78lx//t0v+3S88rFISInD59GqIH2esIJ8G9O2/XVwhjzpw5EAam1xkkBJn/bJX+v1365hxxuCAfH9+3b9/+////48cPuNehNsS7cDEzMTAwMMzb+Q2u4dOnT2vWrMHu9ZtzxP9vl/69RVpCkBlZ3N7enoDXBwEAAA+YYitOilMVAAAAAElFTkSuQmCC
|
|
46
|
+
:target: https://docs.pyansys.com/
|
|
47
|
+
:alt: PyAnsys
|
|
48
|
+
|
|
49
|
+
.. |python| image:: https://img.shields.io/pypi/pyversions/ansys-workbench-core?logo=pypi
|
|
50
|
+
:target: https://pypi.org/project/ansys-workbench-core/
|
|
51
|
+
:alt: Python
|
|
52
|
+
|
|
53
|
+
.. |pypi| image:: https://img.shields.io/pypi/v/ansys-workbench-core.svg?logo=python&logoColor=white
|
|
54
|
+
:target: https://pypi.org/project/ansys-workbench-core
|
|
55
|
+
:alt: PyPI
|
|
56
|
+
|
|
57
|
+
.. |GH-CI| image:: https://github.com/ansys/pyworkbench/actions/workflows/ci.yml/badge.svg
|
|
58
|
+
:target: https://github.com/ansys/pyworkbench/actions/workflows/ci.yml
|
|
59
|
+
:alt: GH-CI
|
|
60
|
+
|
|
61
|
+
.. |MIT| image:: https://img.shields.io/badge/License-MIT-yellow.svg
|
|
62
|
+
:target: https://opensource.org/blog/license/mit
|
|
63
|
+
:alt: MIT
|
|
64
|
+
|
|
65
|
+
.. |ruff| image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
|
|
66
|
+
:target: https://github.com/astral-sh/ruff
|
|
67
|
+
:alt: Ruff
|
|
68
|
+
|
|
69
|
+
PyWorkbench is a Python client library for Ansys Workbench.
|
|
70
|
+
|
|
71
|
+
Installation
|
|
72
|
+
------------
|
|
73
|
+
|
|
74
|
+
To install PyWorkbench in user mode, use `pip <https://pypi.org/project/pip/>`_ :
|
|
75
|
+
|
|
76
|
+
.. code-block:: bash
|
|
77
|
+
|
|
78
|
+
pip install ansys-workbench-core
|
|
79
|
+
|
|
80
|
+
To install the latest development version of PyWorkbench, use the following commands:
|
|
81
|
+
|
|
82
|
+
.. code-block:: bash
|
|
83
|
+
|
|
84
|
+
git clone https://github.com/ansys/pyworkbench
|
|
85
|
+
cd pyworkbench
|
|
86
|
+
pip install -e .
|
|
87
|
+
|
|
88
|
+
For more information, see `Getting started <https://workbench.docs.pyansys.com/version/dev/getting-started.html>`_
|
|
89
|
+
in the PyWorkbench documentation.
|
|
90
|
+
|
|
91
|
+
Documentation and issues
|
|
92
|
+
------------------------
|
|
93
|
+
|
|
94
|
+
Documentation of the latest stable release of PyWorkbench can be found at
|
|
95
|
+
`PyWorkbench documentation <https://workbench.docs.pyansys.com>`_.
|
|
96
|
+
|
|
97
|
+
The documentation has these sections:
|
|
98
|
+
|
|
99
|
+
- `Getting started <https://workbench.docs.pyansys.com/version/dev/getting-started.html>`_: Learn
|
|
100
|
+
how to install PyWorkbench and connect to Workbench.
|
|
101
|
+
- `User guide <https://workbench.docs.pyansys.com/version/dev/user-guide.html>`_: Understand key
|
|
102
|
+
concepts and approaches for using PyWorkbench with the Workbench gRPC service.
|
|
103
|
+
- `API reference <https://workbench.docs.pyansys.com/version/stable/api/index.html>`_: Understand
|
|
104
|
+
how to use Python to interact programmatically with PyWorkbench.
|
|
105
|
+
- `Examples <https://workbench.docs.pyansys.com/version/dev/examples.html>`_: Explore examples that
|
|
106
|
+
show how to use PyWorkbench to create custom applications, automate your existing Workbench
|
|
107
|
+
workflows, and integrate with other popular tools in the Python ecosystem.
|
|
108
|
+
|
|
109
|
+
In the upper right corner of the documentation's title bar, there is an option for switching from
|
|
110
|
+
viewing the documentation for the
|
|
111
|
+
latest stable release to viewing the documentation for the development version or previously released versions.
|
|
112
|
+
|
|
113
|
+
On the `PyWorkbench Issues <https://github.com/ansys/pyworkbench/issues>`_
|
|
114
|
+
page, you can create issues to report bugs and request new features. On the
|
|
115
|
+
`Discussions <https://discuss.ansys.com/>`_ page on the Ansys Developer portal,
|
|
116
|
+
you can post questions, share ideas, and get community feedback.
|
|
117
|
+
|
|
118
|
+
If you have general questions about the PyAnsys ecosystem, email
|
|
119
|
+
`pyansys.core@ansys.com <pyansys.core@ansys.com>`_. If your
|
|
120
|
+
question is specific to PyWorkbench, ask your
|
|
121
|
+
question in an issue as described in the previous paragraph.
|
|
122
|
+
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
PyWorkbench
|
|
2
|
+
===========
|
|
3
|
+
|
|
4
|
+
|pyansys| |python| |pypi| |GH-CI| |MIT| |ruff|
|
|
5
|
+
|
|
6
|
+
.. |pyansys| image:: https://img.shields.io/badge/Py-Ansys-ffc107.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABDklEQVQ4jWNgoDfg5mD8vE7q/3bpVyskbW0sMRUwofHD7Dh5OBkZGBgW7/3W2tZpa2tLQEOyOzeEsfumlK2tbVpaGj4N6jIs1lpsDAwMJ278sveMY2BgCA0NFRISwqkhyQ1q/Nyd3zg4OBgYGNjZ2ePi4rB5loGBhZnhxTLJ/9ulv26Q4uVk1NXV/f///////69du4Zdg78lx//t0v+3S88rFISInD59GqIH2esIJ8G9O2/XVwhjzpw5EAam1xkkBJn/bJX+v1365hxxuCAfH9+3b9/+////48cPuNehNsS7cDEzMTAwMMzb+Q2u4dOnT2vWrMHu9ZtzxP9vl/69RVpCkBlZ3N7enoDXBwEAAA+YYitOilMVAAAAAElFTkSuQmCC
|
|
7
|
+
:target: https://docs.pyansys.com/
|
|
8
|
+
:alt: PyAnsys
|
|
9
|
+
|
|
10
|
+
.. |python| image:: https://img.shields.io/pypi/pyversions/ansys-workbench-core?logo=pypi
|
|
11
|
+
:target: https://pypi.org/project/ansys-workbench-core/
|
|
12
|
+
:alt: Python
|
|
13
|
+
|
|
14
|
+
.. |pypi| image:: https://img.shields.io/pypi/v/ansys-workbench-core.svg?logo=python&logoColor=white
|
|
15
|
+
:target: https://pypi.org/project/ansys-workbench-core
|
|
16
|
+
:alt: PyPI
|
|
17
|
+
|
|
18
|
+
.. |GH-CI| image:: https://github.com/ansys/pyworkbench/actions/workflows/ci.yml/badge.svg
|
|
19
|
+
:target: https://github.com/ansys/pyworkbench/actions/workflows/ci.yml
|
|
20
|
+
:alt: GH-CI
|
|
21
|
+
|
|
22
|
+
.. |MIT| image:: https://img.shields.io/badge/License-MIT-yellow.svg
|
|
23
|
+
:target: https://opensource.org/blog/license/mit
|
|
24
|
+
:alt: MIT
|
|
25
|
+
|
|
26
|
+
.. |ruff| image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
|
|
27
|
+
:target: https://github.com/astral-sh/ruff
|
|
28
|
+
:alt: Ruff
|
|
29
|
+
|
|
30
|
+
PyWorkbench is a Python client library for Ansys Workbench.
|
|
31
|
+
|
|
32
|
+
Installation
|
|
33
|
+
------------
|
|
34
|
+
|
|
35
|
+
To install PyWorkbench in user mode, use `pip <https://pypi.org/project/pip/>`_ :
|
|
36
|
+
|
|
37
|
+
.. code-block:: bash
|
|
38
|
+
|
|
39
|
+
pip install ansys-workbench-core
|
|
40
|
+
|
|
41
|
+
To install the latest development version of PyWorkbench, use the following commands:
|
|
42
|
+
|
|
43
|
+
.. code-block:: bash
|
|
44
|
+
|
|
45
|
+
git clone https://github.com/ansys/pyworkbench
|
|
46
|
+
cd pyworkbench
|
|
47
|
+
pip install -e .
|
|
48
|
+
|
|
49
|
+
For more information, see `Getting started <https://workbench.docs.pyansys.com/version/dev/getting-started.html>`_
|
|
50
|
+
in the PyWorkbench documentation.
|
|
51
|
+
|
|
52
|
+
Documentation and issues
|
|
53
|
+
------------------------
|
|
54
|
+
|
|
55
|
+
Documentation of the latest stable release of PyWorkbench can be found at
|
|
56
|
+
`PyWorkbench documentation <https://workbench.docs.pyansys.com>`_.
|
|
57
|
+
|
|
58
|
+
The documentation has these sections:
|
|
59
|
+
|
|
60
|
+
- `Getting started <https://workbench.docs.pyansys.com/version/dev/getting-started.html>`_: Learn
|
|
61
|
+
how to install PyWorkbench and connect to Workbench.
|
|
62
|
+
- `User guide <https://workbench.docs.pyansys.com/version/dev/user-guide.html>`_: Understand key
|
|
63
|
+
concepts and approaches for using PyWorkbench with the Workbench gRPC service.
|
|
64
|
+
- `API reference <https://workbench.docs.pyansys.com/version/stable/api/index.html>`_: Understand
|
|
65
|
+
how to use Python to interact programmatically with PyWorkbench.
|
|
66
|
+
- `Examples <https://workbench.docs.pyansys.com/version/dev/examples.html>`_: Explore examples that
|
|
67
|
+
show how to use PyWorkbench to create custom applications, automate your existing Workbench
|
|
68
|
+
workflows, and integrate with other popular tools in the Python ecosystem.
|
|
69
|
+
|
|
70
|
+
In the upper right corner of the documentation's title bar, there is an option for switching from
|
|
71
|
+
viewing the documentation for the
|
|
72
|
+
latest stable release to viewing the documentation for the development version or previously released versions.
|
|
73
|
+
|
|
74
|
+
On the `PyWorkbench Issues <https://github.com/ansys/pyworkbench/issues>`_
|
|
75
|
+
page, you can create issues to report bugs and request new features. On the
|
|
76
|
+
`Discussions <https://discuss.ansys.com/>`_ page on the Ansys Developer portal,
|
|
77
|
+
you can post questions, share ideas, and get community feedback.
|
|
78
|
+
|
|
79
|
+
If you have general questions about the PyAnsys ecosystem, email
|
|
80
|
+
`pyansys.core@ansys.com <pyansys.core@ansys.com>`_. If your
|
|
81
|
+
question is specific to PyWorkbench, ask your
|
|
82
|
+
question in an issue as described in the previous paragraph.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["flit_core >=3.4,<4"]
|
|
3
|
+
build-backend = "flit_core.buildapi"
|
|
4
|
+
|
|
5
|
+
# Check https://flit.pypa.io/en/latest/pyproject_toml.html about this config file
|
|
6
|
+
|
|
7
|
+
[project]
|
|
8
|
+
name = "ansys-workbench-core"
|
|
9
|
+
version = "0.4.0"
|
|
10
|
+
description = "A python wrapper for Ansys Workbench"
|
|
11
|
+
readme = "README.rst"
|
|
12
|
+
requires-python = ">=3.9,<4.0"
|
|
13
|
+
license = {file = "LICENSE"}
|
|
14
|
+
authors = [{name = "ANSYS, Inc.", email = "pyansys.core@ansys.com"}]
|
|
15
|
+
maintainers = [{name = "ANSYS, Inc.", email = "pyansys.core@ansys.com"}]
|
|
16
|
+
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 4 - Beta",
|
|
19
|
+
'Topic :: Scientific/Engineering',
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Operating System :: OS Independent",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
dependencies = [
|
|
29
|
+
"ansys_api_workbench>=0.2.0",
|
|
30
|
+
"ansys-platform-instancemanagement>=1.0.1",
|
|
31
|
+
"ansys-pythonnet>=3.1.0rc1",
|
|
32
|
+
"ansys-tools-path>=0.3.1",
|
|
33
|
+
"tqdm>=4.65.0",
|
|
34
|
+
"WMI>=1.4.9",
|
|
35
|
+
]
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
doc = [
|
|
38
|
+
"ansys-sphinx-theme[autoapi]==0.16.5",
|
|
39
|
+
"numpydoc==1.7.0",
|
|
40
|
+
"Sphinx==7.3.7",
|
|
41
|
+
"sphinx-autodoc-typehints==2.1.1",
|
|
42
|
+
"sphinx-copybutton==0.5.2",
|
|
43
|
+
]
|
|
44
|
+
vulnerabilities = [
|
|
45
|
+
"bandit==1.7.8",
|
|
46
|
+
"safety==3.2.3",
|
|
47
|
+
]
|
|
48
|
+
tests = [
|
|
49
|
+
"pytest==8.2.2",
|
|
50
|
+
"pytest-cov==5.0.0",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
[project.urls]
|
|
54
|
+
Documentation = "https://workbench.docs.pyansys.com"
|
|
55
|
+
Source = "https://github.com/ansys/pyworkbench"
|
|
56
|
+
Homepage = "https://github.com/ansys/pyworkbench"
|
|
57
|
+
Tracker = "https://github.com/ansys/pyworkbench/issues"
|
|
58
|
+
|
|
59
|
+
[tool.flit.module]
|
|
60
|
+
name = "ansys.workbench.core"
|
|
61
|
+
|
|
62
|
+
[tool.coverage.run]
|
|
63
|
+
source = ["ansys.workbench"]
|
|
64
|
+
|
|
65
|
+
[tool.coverage.report]
|
|
66
|
+
show_missing = true
|
|
67
|
+
|
|
68
|
+
[tool.pytest.ini_options]
|
|
69
|
+
minversion = "7.1"
|
|
70
|
+
addopts = "-ra --cov=ansys.workbench --cov-report html:.cov/html --cov-report xml:.cov/xml --cov-report term -vv"
|
|
71
|
+
testpaths = ["tests"]
|
|
72
|
+
|
|
73
|
+
[tool.ruff]
|
|
74
|
+
line-length = 100
|
|
75
|
+
extend-exclude = [
|
|
76
|
+
"examples/**/*.py", "tests/scripts/*.py"
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
[tool.ruff.format]
|
|
80
|
+
quote-style = "double"
|
|
81
|
+
indent-style = "space"
|
|
82
|
+
|
|
83
|
+
[tool.ruff.lint]
|
|
84
|
+
select = [
|
|
85
|
+
"E", # pycodestyle, see https://beta.ruff.rs/docs/rules/#pycodestyle-e-w
|
|
86
|
+
"D", # pydocstyle, see https://beta.ruff.rs/docs/rules/#pydocstyle-d
|
|
87
|
+
"F", # pyflakes, see https://beta.ruff.rs/docs/rules/#pyflakes-f
|
|
88
|
+
"I", # isort, see https://beta.ruff.rs/docs/rules/#isort-i
|
|
89
|
+
"N", # pep8-naming, see https://beta.ruff.rs/docs/rules/#pep8-naming-n
|
|
90
|
+
#"PTH", # flake8-use-pathlib, https://beta.ruff.rs/docs/rules/#flake8-use-pathlib-pth
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
#[tool.ruff.lint.extend-per-file-ignores]
|
|
94
|
+
#"tests/*" = ["D100", "D101", "D102", "D103", "PTH"] # ignore docstring requirements in tests
|
|
95
|
+
|
|
96
|
+
[tool.ruff.lint.pydocstyle]
|
|
97
|
+
convention = "numpy"
|
|
98
|
+
|
|
99
|
+
[tool.ruff.lint.isort]
|
|
100
|
+
# Settings: https://docs.astral.sh/ruff/settings/#lintisort
|
|
101
|
+
combine-as-imports = true
|
|
102
|
+
force-sort-within-sections = true
|
|
103
|
+
known-first-party = ["ansys"]
|
|
104
|
+
|
|
105
|
+
[tool.towncrier]
|
|
106
|
+
directory = "doc/changelog.d"
|
|
107
|
+
filename = "doc/source/changelog.rst"
|
|
108
|
+
template = "doc/changelog.d/changelog_template.jinja"
|
|
109
|
+
start_string = ".. towncrier release notes start\n"
|
|
110
|
+
title_format = "`{version} <https://github.com/ansys/pyworkbench/releases/tag/v{version}>`_ - {project_date}"
|
|
111
|
+
issue_format = "`#{issue} <https://github.com/ansys/pyworkbench/pull/{issue}>`_"
|
|
112
|
+
|
|
113
|
+
[[tool.towncrier.type]]
|
|
114
|
+
directory = "added"
|
|
115
|
+
name = "Added"
|
|
116
|
+
showcontent = true
|
|
117
|
+
|
|
118
|
+
[[tool.towncrier.type]]
|
|
119
|
+
directory = "changed"
|
|
120
|
+
name = "Changed"
|
|
121
|
+
showcontent = true
|
|
122
|
+
|
|
123
|
+
[[tool.towncrier.type]]
|
|
124
|
+
directory = "fixed"
|
|
125
|
+
name = "Fixed"
|
|
126
|
+
showcontent = true
|
|
127
|
+
|
|
128
|
+
[[tool.towncrier.type]]
|
|
129
|
+
directory = "dependencies"
|
|
130
|
+
name = "Dependencies"
|
|
131
|
+
showcontent = true
|
|
132
|
+
|
|
133
|
+
[[tool.towncrier.type]]
|
|
134
|
+
directory = "miscellaneous"
|
|
135
|
+
name = "Miscellaneous"
|
|
136
|
+
showcontent = true
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Copyright (C) 2023 - 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
|
+
|
|
23
|
+
"""PyWorkbench client in Python: ansys-workbench-core."""
|
|
24
|
+
|
|
25
|
+
import importlib.metadata as importlib_metadata
|
|
26
|
+
|
|
27
|
+
# exposed API
|
|
28
|
+
from ansys.workbench.core.public_api import connect_workbench, launch_workbench # noqa: F401
|
|
29
|
+
|
|
30
|
+
# Read from the pyproject.toml
|
|
31
|
+
__version__ = importlib_metadata.version("ansys-workbench-core")
|
|
32
|
+
"""Version of ansys-workbench-core module."""
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Copyright (C) 2023 - 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
|
+
|
|
23
|
+
"""Module for downloading example data from the ``example-data`` repository."""
|
|
24
|
+
|
|
25
|
+
import logging
|
|
26
|
+
import os
|
|
27
|
+
import shutil
|
|
28
|
+
import urllib.request
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ExampleData:
|
|
32
|
+
"""Provides for downloading example data from the ``example-data`` repository."""
|
|
33
|
+
|
|
34
|
+
def _get_file_url(relative_file_path): # noqa: N805
|
|
35
|
+
"""Get the URL of the file in the ``example-data`` repository.
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
filename : str
|
|
40
|
+
Name of the file.
|
|
41
|
+
dirname : str
|
|
42
|
+
Name of the directory containing the file.
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
str
|
|
47
|
+
URL of the file in the ``example-data`` repository.
|
|
48
|
+
"""
|
|
49
|
+
return f"https://github.com/ansys/example-data/tree/master/pyworkbench/{relative_file_path}"
|
|
50
|
+
|
|
51
|
+
def __retrieve_file(url, local_file_path): # noqa: N805
|
|
52
|
+
"""Download the file from the URL.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
url : str
|
|
57
|
+
URL of the file.
|
|
58
|
+
local_file_path : str
|
|
59
|
+
Local path to save the file to.
|
|
60
|
+
|
|
61
|
+
Raises
|
|
62
|
+
------
|
|
63
|
+
Exception
|
|
64
|
+
If the URL is not accessible.
|
|
65
|
+
"""
|
|
66
|
+
logging.info(f"Downloading {url} from ``example-data`` repository ...")
|
|
67
|
+
|
|
68
|
+
with urllib.request.urlopen(url) as in_stream:
|
|
69
|
+
if in_stream.code != 200:
|
|
70
|
+
raise Exception("error getting the url, code " + str(in_stream.code))
|
|
71
|
+
with open(local_file_path, "wb") as out_file:
|
|
72
|
+
shutil.copyfileobj(in_stream, out_file)
|
|
73
|
+
logging.info(f"Downloaded the file as {local_file_path}.")
|
|
74
|
+
|
|
75
|
+
def download(relative_file_path, local_dir_path): # noqa: N805
|
|
76
|
+
"""Download the file from the ``example-data`` repository.
|
|
77
|
+
|
|
78
|
+
Parameters
|
|
79
|
+
----------
|
|
80
|
+
relative_file_path : str
|
|
81
|
+
File path relative to the ``pyworkbench`` folder in the ``example-data`` repository.
|
|
82
|
+
local_dir_path : str
|
|
83
|
+
Local path to the directory to save the file to.
|
|
84
|
+
|
|
85
|
+
Returns
|
|
86
|
+
-------
|
|
87
|
+
str
|
|
88
|
+
Name of the downloaded file.
|
|
89
|
+
"""
|
|
90
|
+
url = ExampleData._get_file_url(relative_file_path)
|
|
91
|
+
downloaded_file_name = os.path.basename(relative_file_path)
|
|
92
|
+
local_file_path = os.path.join(local_dir_path, downloaded_file_name)
|
|
93
|
+
ExampleData.__retrieve_file(url, local_file_path)
|
|
94
|
+
return downloaded_file_name
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
__all__ = []
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
# Copyright (C) 2023 - 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
|
+
|
|
23
|
+
"""Module for launching a Workbench server on a local or remote Windows machine."""
|
|
24
|
+
|
|
25
|
+
import logging
|
|
26
|
+
import os
|
|
27
|
+
import platform
|
|
28
|
+
import tempfile
|
|
29
|
+
import time
|
|
30
|
+
import uuid
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
if platform.system() == "Windows":
|
|
34
|
+
import wmi
|
|
35
|
+
except ImportError:
|
|
36
|
+
# Handle the case when 'wmi' cannot be imported
|
|
37
|
+
wmi = None
|
|
38
|
+
|
|
39
|
+
from ansys.workbench.core.workbench_client import WorkbenchClient
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ClientWrapper(WorkbenchClient):
|
|
43
|
+
"""Provides for connecting to a Workbench server.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
port : int
|
|
48
|
+
Port used by the server.
|
|
49
|
+
client_workdir : str, default: None
|
|
50
|
+
Path to a writable directory on the client computer.
|
|
51
|
+
host : str, default: None
|
|
52
|
+
Server computer's name or IP address.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def __init__(self, port, client_workdir=None, host=None):
|
|
56
|
+
"""Create a PyWorkbench client that connects to a Workbench server."""
|
|
57
|
+
if host is None:
|
|
58
|
+
host = "localhost"
|
|
59
|
+
if client_workdir is None:
|
|
60
|
+
client_workdir = tempfile.gettempdir()
|
|
61
|
+
super().__init__(client_workdir, host, port)
|
|
62
|
+
super()._connect()
|
|
63
|
+
|
|
64
|
+
def exit(self):
|
|
65
|
+
"""Disconnect from the server."""
|
|
66
|
+
if super()._is_connected():
|
|
67
|
+
super()._disconnect()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class LaunchWorkbench(ClientWrapper):
|
|
71
|
+
"""Launch a Workbench server on a local or remote Windows machine.
|
|
72
|
+
|
|
73
|
+
Parameters
|
|
74
|
+
----------
|
|
75
|
+
release : str, default: "242"
|
|
76
|
+
Workbench release to launch.
|
|
77
|
+
client_workdir : str, default: None
|
|
78
|
+
Path to a writable directory on the client computer. The default is ``None``,
|
|
79
|
+
in which case the system temp directory is used.
|
|
80
|
+
server_workdir : str, None
|
|
81
|
+
Path to a writable directory on the server computer. The default is ``None``,
|
|
82
|
+
in which case the user preference for the Workbench temporary file folder is used.
|
|
83
|
+
host : str, None
|
|
84
|
+
Server computer's name or IP address. The default is ``None`` for launching on the
|
|
85
|
+
local computer.
|
|
86
|
+
username : str, None
|
|
87
|
+
User's login name on the server computer. The default is ``None`` for launching on
|
|
88
|
+
the local computer.
|
|
89
|
+
password : str, None
|
|
90
|
+
User's password on the server computer. The default is ``None`` for launching on
|
|
91
|
+
the local computer.
|
|
92
|
+
|
|
93
|
+
Raises
|
|
94
|
+
------
|
|
95
|
+
Exception
|
|
96
|
+
If the Ansys release number is invalid.
|
|
97
|
+
|
|
98
|
+
Examples
|
|
99
|
+
--------
|
|
100
|
+
Launch a server on the local computer and use the ``wb`` variable to hold the returned client.
|
|
101
|
+
|
|
102
|
+
>>> from ansys.workbench.core import launch_workbench
|
|
103
|
+
>>> wb = launch_workbench()
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
def __init__(
|
|
107
|
+
self,
|
|
108
|
+
release="242",
|
|
109
|
+
client_workdir=None,
|
|
110
|
+
server_workdir=None,
|
|
111
|
+
host=None,
|
|
112
|
+
username=None,
|
|
113
|
+
password=None,
|
|
114
|
+
):
|
|
115
|
+
self._wmi_connection = None
|
|
116
|
+
self._process_id = -1
|
|
117
|
+
|
|
118
|
+
if (
|
|
119
|
+
len(release) != 3
|
|
120
|
+
or not release.isdigit()
|
|
121
|
+
or release[0] not in ["2", "3"]
|
|
122
|
+
or release[2] not in ["1", "2"]
|
|
123
|
+
):
|
|
124
|
+
raise Exception("Invalid ANSYS release: " + release)
|
|
125
|
+
port = self.__launch_server(host, release, server_workdir, username, password)
|
|
126
|
+
if port is None or port <= 0:
|
|
127
|
+
raise Exception("Filed to launch Ansys Workbench service.")
|
|
128
|
+
super().__init__(port, client_workdir, host)
|
|
129
|
+
|
|
130
|
+
def __launch_server(self, host, release, server_workdir, username, password):
|
|
131
|
+
"""Launch a Workbench server on the local or a remote Windows machine."""
|
|
132
|
+
try:
|
|
133
|
+
if host is None:
|
|
134
|
+
self._wmi_connection = wmi.WMI()
|
|
135
|
+
else:
|
|
136
|
+
if username is None or password is None:
|
|
137
|
+
raise Exception(
|
|
138
|
+
"Username and passwork must be specified "
|
|
139
|
+
"to launch Workbench on a remote machine."
|
|
140
|
+
)
|
|
141
|
+
self._wmi_connection = wmi.WMI(host, user=username, password=password)
|
|
142
|
+
logging.info("Host connection is established.")
|
|
143
|
+
|
|
144
|
+
install_path = None
|
|
145
|
+
for ev in self._wmi_connection.Win32_Environment():
|
|
146
|
+
if ev.Name == "AWP_ROOT" + release:
|
|
147
|
+
install_path = ev.VariableValue
|
|
148
|
+
break
|
|
149
|
+
if install_path is None:
|
|
150
|
+
install_path = "C:/Program Files/Ansys Inc/v" + release
|
|
151
|
+
logging.warning(
|
|
152
|
+
"Ansys installation is not found. Assume the default location: " + install_path
|
|
153
|
+
)
|
|
154
|
+
else:
|
|
155
|
+
logging.info("Ansys installation is found at: " + install_path)
|
|
156
|
+
executable = os.path.join(install_path, "Framework", "bin", "Win64", "RunWB2.exe")
|
|
157
|
+
prefix = uuid.uuid4().hex
|
|
158
|
+
workdir_arg = ""
|
|
159
|
+
if server_workdir is not None:
|
|
160
|
+
# use forward slash only to avoid escaping as command line argument
|
|
161
|
+
server_workdir = server_workdir.replace("\\", "/")
|
|
162
|
+
workdir_arg = ",WorkingDirectory='" + server_workdir + "'"
|
|
163
|
+
command = (
|
|
164
|
+
executable
|
|
165
|
+
+ " -I -E \"StartServer(EnvironmentPrefix='"
|
|
166
|
+
+ prefix
|
|
167
|
+
+ "'"
|
|
168
|
+
+ workdir_arg
|
|
169
|
+
+ ')"'
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
process_startup_info = self._wmi_connection.Win32_ProcessStartup.new(ShowWindow=1)
|
|
173
|
+
process_id, result = self._wmi_connection.Win32_Process.Create(
|
|
174
|
+
CommandLine=command, ProcessStartupInformation=process_startup_info
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
if result == 0:
|
|
178
|
+
logging.info("Workbench launched on the host with process id: " + str(process_id))
|
|
179
|
+
self._process_id = process_id
|
|
180
|
+
else:
|
|
181
|
+
logging.error("Workbench failed to launch on the host.")
|
|
182
|
+
return 0
|
|
183
|
+
|
|
184
|
+
# retrieve server port once WB is fully up running
|
|
185
|
+
port = None
|
|
186
|
+
timeout = 60 * 8 # set 8 minutes as upper limit for WB startup
|
|
187
|
+
start_time = time.time()
|
|
188
|
+
while True:
|
|
189
|
+
for ev in self._wmi_connection.Win32_Environment():
|
|
190
|
+
if ev.Name == "ANSYS_FRAMEWORK_SERVER_PORT":
|
|
191
|
+
port = ev.VariableValue
|
|
192
|
+
if port.startswith(prefix):
|
|
193
|
+
port = port[len(prefix) :]
|
|
194
|
+
break
|
|
195
|
+
else:
|
|
196
|
+
port = None
|
|
197
|
+
break
|
|
198
|
+
if port is not None:
|
|
199
|
+
break
|
|
200
|
+
if time.time() - start_time > timeout:
|
|
201
|
+
logging.error("Failed to start Workbench service within reasonable timeout.")
|
|
202
|
+
break
|
|
203
|
+
time.sleep(10)
|
|
204
|
+
if port is None:
|
|
205
|
+
logging.error("Failed to retrieve the port used by Workbench service.")
|
|
206
|
+
else:
|
|
207
|
+
logging.info("Workbench service uses port " + port)
|
|
208
|
+
|
|
209
|
+
return int(port)
|
|
210
|
+
|
|
211
|
+
except wmi.x_wmi:
|
|
212
|
+
logging.error("wrong credential")
|
|
213
|
+
|
|
214
|
+
def exit(self):
|
|
215
|
+
"""Terminate the Workbench server and disconnect the client."""
|
|
216
|
+
super().exit()
|
|
217
|
+
|
|
218
|
+
if self._wmi_connection is None:
|
|
219
|
+
return
|
|
220
|
+
|
|
221
|
+
# collect parent-children mapping
|
|
222
|
+
children = {self._process_id: []}
|
|
223
|
+
process_by_id = {}
|
|
224
|
+
for p in self._wmi_connection.Win32_Process():
|
|
225
|
+
process_by_id[p.ProcessId] = p
|
|
226
|
+
children.setdefault(p.ProcessId, [])
|
|
227
|
+
if p.ParentProcessId is None:
|
|
228
|
+
continue
|
|
229
|
+
children.setdefault(p.ParentProcessId, [])
|
|
230
|
+
children[p.ParentProcessId].append(p.ProcessId)
|
|
231
|
+
|
|
232
|
+
# terminate related processes bottom-up
|
|
233
|
+
to_terminate = []
|
|
234
|
+
this_level = set([self._process_id])
|
|
235
|
+
while True:
|
|
236
|
+
next_level = set()
|
|
237
|
+
for p in this_level:
|
|
238
|
+
next_level.update(children[p])
|
|
239
|
+
if len(next_level) == 0:
|
|
240
|
+
break
|
|
241
|
+
to_terminate.append(next_level)
|
|
242
|
+
this_level = next_level
|
|
243
|
+
for ps in reversed(to_terminate):
|
|
244
|
+
for p in ps:
|
|
245
|
+
logging.info("Shutting down " + process_by_id[p].Name + " ...")
|
|
246
|
+
try:
|
|
247
|
+
process_by_id[p].Terminate()
|
|
248
|
+
except Exception:
|
|
249
|
+
pass
|
|
250
|
+
|
|
251
|
+
logging.info("Workbench server connection has ended.")
|
|
252
|
+
self._wmi_connection = None
|
|
253
|
+
self._process_id = -1
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def launch_workbench(
|
|
257
|
+
release="242", client_workdir=None, server_workdir=None, host=None, username=None, password=None
|
|
258
|
+
):
|
|
259
|
+
"""Launch PyWorkbench server on the local or a remote Windows machine.
|
|
260
|
+
|
|
261
|
+
This method launch a Workbench server on the local or a remote Windows machine and creates
|
|
262
|
+
a PyWorkbench client that connects to the server.
|
|
263
|
+
|
|
264
|
+
Parameters
|
|
265
|
+
----------
|
|
266
|
+
release : str, default: "242"
|
|
267
|
+
Workbench release to launch.
|
|
268
|
+
client_workdir : str, default: None
|
|
269
|
+
Path to a writable directory on the client computer. The default is ``None``,
|
|
270
|
+
in which case the system temp directory is used.
|
|
271
|
+
server_workdir : str, None
|
|
272
|
+
Path to a writable directory on the server computer. The default is ``None``,
|
|
273
|
+
in which case the user preference for the Workbench temporary file folder is used.
|
|
274
|
+
host : str, None
|
|
275
|
+
Server computer's name or IP address. The default is ``None`` for launching on the
|
|
276
|
+
local computer.
|
|
277
|
+
username : str, None
|
|
278
|
+
User's login name on the server computer. The default is ``None`` for launching on
|
|
279
|
+
the local computer.
|
|
280
|
+
password : str, None
|
|
281
|
+
User's password on the server computer. The default is ``None`` for launching on
|
|
282
|
+
the local computer.
|
|
283
|
+
|
|
284
|
+
Returns
|
|
285
|
+
-------
|
|
286
|
+
ClientWrapper
|
|
287
|
+
Instance of the PyWorkbench client that is connected to the launched server.
|
|
288
|
+
|
|
289
|
+
Examples
|
|
290
|
+
--------
|
|
291
|
+
Launch a server on the local computer and use the ``wb`` variable to hold the returned client.
|
|
292
|
+
|
|
293
|
+
>>> from ansys.workbench.core import launch_workbench
|
|
294
|
+
>>> wb = launch_workbench()
|
|
295
|
+
|
|
296
|
+
"""
|
|
297
|
+
return LaunchWorkbench(release, client_workdir, server_workdir, host, username, password)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def connect_workbench(port, client_workdir=None, host=None):
|
|
301
|
+
"""Create a PyWorkbench client that connects to an already running Workbench server.
|
|
302
|
+
|
|
303
|
+
Parameters
|
|
304
|
+
----------
|
|
305
|
+
port : int
|
|
306
|
+
Port used by the server.
|
|
307
|
+
client_workdir : str, default: None
|
|
308
|
+
Path to a writable directory on the client computer. The default is ``None``,
|
|
309
|
+
in which case the system temp directory is used.
|
|
310
|
+
host : str, default: None
|
|
311
|
+
Server computer's name or IP address. The default is ``None`` for the local computer.
|
|
312
|
+
|
|
313
|
+
Returns
|
|
314
|
+
-------
|
|
315
|
+
ClientWrapper
|
|
316
|
+
Instance of the PyWorkbench client that is connected to the server.
|
|
317
|
+
|
|
318
|
+
Examples
|
|
319
|
+
--------
|
|
320
|
+
Connect to a server at port 32588 on localhost and use the ``wb`` variable to hold the
|
|
321
|
+
returned client.
|
|
322
|
+
|
|
323
|
+
>>> from ansys.workbench.core import connect_workbench
|
|
324
|
+
>>> wb = connect_workbench(port = 32588)
|
|
325
|
+
"""
|
|
326
|
+
return ClientWrapper(port, client_workdir, host)
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
__all__ = ["launch_workbench", "connect_workbench"]
|
|
File without changes
|
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
# Copyright (C) 2023 - 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
|
+
|
|
23
|
+
"""Workbench client module for PyWorkbench."""
|
|
24
|
+
|
|
25
|
+
import glob
|
|
26
|
+
import json
|
|
27
|
+
import logging
|
|
28
|
+
import logging.handlers
|
|
29
|
+
from logging.handlers import WatchedFileHandler
|
|
30
|
+
import os
|
|
31
|
+
|
|
32
|
+
import grpc
|
|
33
|
+
import tqdm
|
|
34
|
+
|
|
35
|
+
from ansys.api.workbench.v0 import workbench_pb2 as wb
|
|
36
|
+
from ansys.api.workbench.v0.workbench_pb2_grpc import WorkbenchServiceStub
|
|
37
|
+
from ansys.workbench.core.example_data import ExampleData
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class WorkbenchClient:
|
|
41
|
+
"""Provides the PyWorkbench client.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
local_workdir : str
|
|
46
|
+
Local working directory for the client.
|
|
47
|
+
server_host : str
|
|
48
|
+
Hostname or IP address of the server.
|
|
49
|
+
server_port : int
|
|
50
|
+
Port number of the server.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(self, local_workdir, server_host, server_port):
|
|
54
|
+
"""Create a Workbench client."""
|
|
55
|
+
self.workdir = local_workdir
|
|
56
|
+
self._server_host = server_host
|
|
57
|
+
self._server_port = server_port
|
|
58
|
+
self.__init_logging()
|
|
59
|
+
|
|
60
|
+
def __enter__(self):
|
|
61
|
+
"""Connect to the server when entering a context."""
|
|
62
|
+
self._connect()
|
|
63
|
+
return self
|
|
64
|
+
|
|
65
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
66
|
+
"""Disconnect from the server when exiting a context."""
|
|
67
|
+
self._disconnect()
|
|
68
|
+
|
|
69
|
+
def _connect(self):
|
|
70
|
+
"""Connect to the server."""
|
|
71
|
+
hnp = self._server_host + ":" + str(self._server_port)
|
|
72
|
+
self.channel = grpc.insecure_channel(hnp)
|
|
73
|
+
self.stub = WorkbenchServiceStub(self.channel)
|
|
74
|
+
logging.info("connected to the WB server at " + hnp)
|
|
75
|
+
|
|
76
|
+
def _disconnect(self):
|
|
77
|
+
"""Disconnect from the server."""
|
|
78
|
+
if self.channel:
|
|
79
|
+
self.channel.close()
|
|
80
|
+
self.channel = None
|
|
81
|
+
self.stub = None
|
|
82
|
+
logging.info("Disconnected from the Workbench server")
|
|
83
|
+
|
|
84
|
+
def _is_connected(self):
|
|
85
|
+
"""Return whether this client is connected to the server."""
|
|
86
|
+
return self.channel is not None
|
|
87
|
+
|
|
88
|
+
def __init_logging(self):
|
|
89
|
+
"""Initialize logging for the client."""
|
|
90
|
+
self._logger = logging.getLogger("WB")
|
|
91
|
+
self._logger.setLevel(logging.DEBUG)
|
|
92
|
+
self._logger.propagate = False
|
|
93
|
+
stream_handler = logging.StreamHandler()
|
|
94
|
+
stream_handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
|
|
95
|
+
stream_handler.setLevel(logging.WARNING)
|
|
96
|
+
self._logger.addHandler(stream_handler)
|
|
97
|
+
self.__log_console_handler = stream_handler
|
|
98
|
+
|
|
99
|
+
def set_console_log_level(self, log_level):
|
|
100
|
+
"""Set the log filter level for the client console.
|
|
101
|
+
|
|
102
|
+
Parameters
|
|
103
|
+
----------
|
|
104
|
+
log_level : str, default: "error"
|
|
105
|
+
Level of logging. Options are "critical" "debug", "error", "info", and "warning".
|
|
106
|
+
"""
|
|
107
|
+
self.__log_console_handler.setLevel(WorkbenchClient.__to_python_log_level(log_level))
|
|
108
|
+
|
|
109
|
+
def set_log_file(self, log_file):
|
|
110
|
+
"""Set a local log file for the Workbench server log.
|
|
111
|
+
|
|
112
|
+
Create a locl log file if one does not exist and append it to the existing log file.
|
|
113
|
+
|
|
114
|
+
Parameters
|
|
115
|
+
----------
|
|
116
|
+
log_file : str
|
|
117
|
+
Path to a local file to use for logging.
|
|
118
|
+
"""
|
|
119
|
+
self.reset_log_file()
|
|
120
|
+
|
|
121
|
+
file_handler = WatchedFileHandler(log_file)
|
|
122
|
+
file_handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
|
|
123
|
+
file_handler.setLevel(logging.DEBUG)
|
|
124
|
+
self._logger.addHandler(file_handler)
|
|
125
|
+
self.__log_file_handler = file_handler
|
|
126
|
+
|
|
127
|
+
def reset_log_file(self):
|
|
128
|
+
"""No longer use the current log file for the Workbench server log."""
|
|
129
|
+
if self.__log_file_handler is None:
|
|
130
|
+
return
|
|
131
|
+
self.__log_file_handler.close()
|
|
132
|
+
self._logger.removeHandler(self.__log_file_handler)
|
|
133
|
+
self.__log_file_handler = None
|
|
134
|
+
|
|
135
|
+
__log_file_handler = None
|
|
136
|
+
__log_console_handler = None
|
|
137
|
+
|
|
138
|
+
def run_script_string(self, script_string, log_level="error"):
|
|
139
|
+
"""Run a script on the server.
|
|
140
|
+
|
|
141
|
+
Parameters
|
|
142
|
+
----------
|
|
143
|
+
script_string : str
|
|
144
|
+
String containing the content of the script to run.
|
|
145
|
+
log_level : str, default: "error"
|
|
146
|
+
Level of logging. Options are "critical" "debug", "error", "info", and "warning".
|
|
147
|
+
|
|
148
|
+
Returns
|
|
149
|
+
-------
|
|
150
|
+
str
|
|
151
|
+
Output defined in the script.
|
|
152
|
+
|
|
153
|
+
Examples
|
|
154
|
+
--------
|
|
155
|
+
Run a Workbench script, given in a string, that returns the name of
|
|
156
|
+
a newly created system.
|
|
157
|
+
|
|
158
|
+
>>> wb.run_script_string(r'''import json
|
|
159
|
+
wb_script_result=json.dumps(GetTemplate(TemplateName="FLUENT").CreateSystem().Name)
|
|
160
|
+
''')
|
|
161
|
+
|
|
162
|
+
"""
|
|
163
|
+
if not self._is_connected():
|
|
164
|
+
logging.error("Workbench client is not yet connected to a server.")
|
|
165
|
+
request = wb.RunScriptRequest(
|
|
166
|
+
content=script_string, log_level=WorkbenchClient.__to_server_log_level(log_level)
|
|
167
|
+
)
|
|
168
|
+
for response in self.stub.RunScript(request):
|
|
169
|
+
if response.log and response.log.messages and len(response.log.messages) > 0:
|
|
170
|
+
for log_entry in response.log.messages:
|
|
171
|
+
self.__python_logging(log_entry.level, log_entry.message)
|
|
172
|
+
if response.result:
|
|
173
|
+
if response.result.error:
|
|
174
|
+
logging.error("Error when running the script: " + response.result.error)
|
|
175
|
+
return None
|
|
176
|
+
elif response.result.result:
|
|
177
|
+
logging.info("The script has finisished.")
|
|
178
|
+
return json.loads(response.result.result)
|
|
179
|
+
|
|
180
|
+
def run_script_file(self, script_file_name, log_level="error"):
|
|
181
|
+
"""Run a script file on the server.
|
|
182
|
+
|
|
183
|
+
Parameters
|
|
184
|
+
----------
|
|
185
|
+
script_file_name : str
|
|
186
|
+
Name of the script file to run. The script file is located in the client
|
|
187
|
+
working directory
|
|
188
|
+
log_level : str, default: "error"
|
|
189
|
+
Level of logging. Options are "critical" "debug", "error", "info", and "warning".
|
|
190
|
+
|
|
191
|
+
Returns
|
|
192
|
+
-------
|
|
193
|
+
str
|
|
194
|
+
Output defined in the script.
|
|
195
|
+
"""
|
|
196
|
+
if not self._is_connected():
|
|
197
|
+
logging.error("Workbench client is not yet connected to a server")
|
|
198
|
+
script_path = os.path.join(self.workdir, script_file_name)
|
|
199
|
+
with open(script_path, encoding="utf-8-sig") as sf:
|
|
200
|
+
script_string = sf.read()
|
|
201
|
+
return self.run_script_string(script_string, log_level)
|
|
202
|
+
|
|
203
|
+
def upload_file(self, *file_list, show_progress=True):
|
|
204
|
+
"""Upload one or more files from the client to the server.
|
|
205
|
+
|
|
206
|
+
Parameters
|
|
207
|
+
----------
|
|
208
|
+
file_list : list[str]
|
|
209
|
+
List of paths to the one or more local files to upload. The
|
|
210
|
+
wildcard characters "?" and "*" are supported.
|
|
211
|
+
show_progress : bool, default: True
|
|
212
|
+
Whether to show a progress bar during the upload.
|
|
213
|
+
|
|
214
|
+
Returns
|
|
215
|
+
-------
|
|
216
|
+
list[str]
|
|
217
|
+
Names of the uploaded file.
|
|
218
|
+
"""
|
|
219
|
+
if not self._is_connected():
|
|
220
|
+
logging.error("Workbench client is not yet connected to a server.")
|
|
221
|
+
requested = []
|
|
222
|
+
for file_pattern in file_list:
|
|
223
|
+
if "*" in file_pattern or "?" in file_pattern:
|
|
224
|
+
if not os.path.isabs(file_pattern):
|
|
225
|
+
file_pattern = os.path.join(self.workdir, file_pattern)
|
|
226
|
+
requested.extend(glob.glob(file_pattern))
|
|
227
|
+
else:
|
|
228
|
+
requested.append(file_pattern)
|
|
229
|
+
existing_files = []
|
|
230
|
+
nonexisting_files = []
|
|
231
|
+
for file_name in requested:
|
|
232
|
+
if not os.path.isabs(file_name):
|
|
233
|
+
file_name = os.path.join(self.workdir, file_name)
|
|
234
|
+
if os.path.isfile(file_name):
|
|
235
|
+
existing_files.append(file_name)
|
|
236
|
+
else:
|
|
237
|
+
nonexisting_files.append(file_name)
|
|
238
|
+
if len(nonexisting_files) > 0:
|
|
239
|
+
logging.warning(
|
|
240
|
+
"The following files do not exist and are skipped: " + "\n".join(nonexisting_files)
|
|
241
|
+
)
|
|
242
|
+
for file_path in existing_files:
|
|
243
|
+
logging.info(f"uploading file {file_path}")
|
|
244
|
+
response = self.stub.UploadFile(self.__upload_iterator(file_path, show_progress))
|
|
245
|
+
if response.error:
|
|
246
|
+
logging.error("Error during file upload: " + response.error)
|
|
247
|
+
else:
|
|
248
|
+
logging.info(
|
|
249
|
+
"A file is uploaded to the server with the name: " + response.file_name
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
def __upload_iterator(self, file_path, show_progress):
|
|
253
|
+
dir_path, file_name = os.path.split(file_path)
|
|
254
|
+
yield wb.UploadFileRequest(file_name=file_name)
|
|
255
|
+
|
|
256
|
+
pbar = None
|
|
257
|
+
if show_progress:
|
|
258
|
+
bytes = os.path.getsize(file_path)
|
|
259
|
+
pbar = tqdm.tqdm(
|
|
260
|
+
total=bytes,
|
|
261
|
+
desc=f"Uploading {file_name}",
|
|
262
|
+
unit="B",
|
|
263
|
+
unit_scale=True,
|
|
264
|
+
unit_divisor=1024,
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
chunk_size = 65536 ## 64 kb
|
|
268
|
+
with open(file_path, mode="rb") as f:
|
|
269
|
+
while True:
|
|
270
|
+
chunk = f.read(chunk_size)
|
|
271
|
+
if chunk:
|
|
272
|
+
if pbar is not None:
|
|
273
|
+
pbar.update(len(chunk))
|
|
274
|
+
yield wb.UploadFileRequest(file_content=chunk)
|
|
275
|
+
else:
|
|
276
|
+
if pbar is not None:
|
|
277
|
+
pbar.close()
|
|
278
|
+
return
|
|
279
|
+
|
|
280
|
+
def upload_file_from_example_repo(self, relative_file_path, show_progress=True):
|
|
281
|
+
"""Upload a file from the Ansys ``example-data`` repository to the server.
|
|
282
|
+
|
|
283
|
+
Parameters
|
|
284
|
+
----------
|
|
285
|
+
relative_file_path : str
|
|
286
|
+
File path relative to the ``pyworkbench`` folder in the ``example-data`` repository.
|
|
287
|
+
show_progress : bool, default: True
|
|
288
|
+
Whether to show a progress bar during the upload.
|
|
289
|
+
"""
|
|
290
|
+
if not self._is_connected():
|
|
291
|
+
logging.error("Workbench client is not yet connected to a server.")
|
|
292
|
+
downloaded = ExampleData.download(relative_file_path, self.workdir)
|
|
293
|
+
self.upload_file(downloaded, show_progress=show_progress)
|
|
294
|
+
|
|
295
|
+
def download_file(self, file_name, show_progress=True, target_dir=None):
|
|
296
|
+
"""Download one or more files from the server.
|
|
297
|
+
|
|
298
|
+
Parameters
|
|
299
|
+
----------
|
|
300
|
+
file_name : str
|
|
301
|
+
Name of the file. File must be located in the server's working directory.
|
|
302
|
+
The wildcard characters "?" and "*" are supported. A ZIP file is automatically
|
|
303
|
+
generated and downloaded when multiple files are specified.
|
|
304
|
+
show_progress : bool, default: True
|
|
305
|
+
Whether to show a progress bar during the download.
|
|
306
|
+
target_dir : str, default: None
|
|
307
|
+
Path to a local directory to download the files to. The default is ``None``,
|
|
308
|
+
in which case the client working directory is used.
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
Returns
|
|
312
|
+
-------
|
|
313
|
+
str
|
|
314
|
+
Names of the one or more downloaded files.
|
|
315
|
+
"""
|
|
316
|
+
if not self._is_connected():
|
|
317
|
+
logging.error("Workbench client is not yet connected to a server.")
|
|
318
|
+
request = wb.DownloadFileRequest(file_name=file_name)
|
|
319
|
+
file_name = file_name.replace("*", "_").replace("?", "_")
|
|
320
|
+
td = target_dir
|
|
321
|
+
if td is None:
|
|
322
|
+
td = self.workdir
|
|
323
|
+
file_path = os.path.join(td, file_name)
|
|
324
|
+
pbar = None
|
|
325
|
+
started = False
|
|
326
|
+
for response in self.stub.DownloadFile(request):
|
|
327
|
+
if response.error:
|
|
328
|
+
logging.error("Error during file download: " + response.error)
|
|
329
|
+
return None
|
|
330
|
+
if response.file_info:
|
|
331
|
+
if response.file_info.is_archive:
|
|
332
|
+
file_name += ".zip"
|
|
333
|
+
file_path += ".zip"
|
|
334
|
+
if response.file_info.file_size > 0:
|
|
335
|
+
if show_progress:
|
|
336
|
+
pbar = tqdm.tqdm(
|
|
337
|
+
total=response.file_info.file_size,
|
|
338
|
+
desc=f"Downloading {file_name}",
|
|
339
|
+
unit="B",
|
|
340
|
+
unit_scale=True,
|
|
341
|
+
unit_divisor=1024,
|
|
342
|
+
)
|
|
343
|
+
if response.file_content:
|
|
344
|
+
size = len(response.file_content)
|
|
345
|
+
if size > 0:
|
|
346
|
+
if not started:
|
|
347
|
+
if os.path.exists(file_path):
|
|
348
|
+
os.remove(file_path)
|
|
349
|
+
started = True
|
|
350
|
+
with open(file_path, mode="ab") as f:
|
|
351
|
+
f.write(response.file_content)
|
|
352
|
+
if pbar is not None:
|
|
353
|
+
pbar.update(size)
|
|
354
|
+
logging.info(f"Dwnloaded the file {file_name}.")
|
|
355
|
+
if pbar is not None:
|
|
356
|
+
pbar.close()
|
|
357
|
+
return file_name
|
|
358
|
+
|
|
359
|
+
def __python_logging(self, log_level, msg):
|
|
360
|
+
"""Log a message with the given log level.
|
|
361
|
+
|
|
362
|
+
Parameters
|
|
363
|
+
----------
|
|
364
|
+
log_level : int
|
|
365
|
+
log level. Options are ``logging.CRITICAL``, ``logging.DEBUG``, ``logging.ERROR``,
|
|
366
|
+
``logging.INFO``, and ``logging.WARNING``.
|
|
367
|
+
|
|
368
|
+
msg : str
|
|
369
|
+
Message to log.
|
|
370
|
+
"""
|
|
371
|
+
if log_level == wb.LOG_DEBUG:
|
|
372
|
+
self._logger.debug(msg)
|
|
373
|
+
elif log_level == wb.LOG_INFO:
|
|
374
|
+
self._logger.info(msg)
|
|
375
|
+
elif log_level == wb.LOG_WARNING:
|
|
376
|
+
self._logger.warn(msg)
|
|
377
|
+
elif log_level == wb.LOG_ERROR:
|
|
378
|
+
self._logger.error(msg)
|
|
379
|
+
elif log_level == wb.LOG_FATAL:
|
|
380
|
+
self._logger.fatal(msg)
|
|
381
|
+
|
|
382
|
+
@staticmethod
|
|
383
|
+
def __to_python_log_level(log_level):
|
|
384
|
+
"""Convert the given log level to the corresponding Python log level.
|
|
385
|
+
|
|
386
|
+
Parameters
|
|
387
|
+
----------
|
|
388
|
+
log_level : str
|
|
389
|
+
level of logging: options are "debug", "info", "warning", "error", "critical"
|
|
390
|
+
|
|
391
|
+
Returns
|
|
392
|
+
-------
|
|
393
|
+
int
|
|
394
|
+
the corresponding Python log level
|
|
395
|
+
"""
|
|
396
|
+
log_level = log_level.lower()
|
|
397
|
+
for level_name, server_level in WorkbenchClient.__log_levels.items():
|
|
398
|
+
if log_level in level_name:
|
|
399
|
+
return server_level[1]
|
|
400
|
+
return logging.NOTSET
|
|
401
|
+
|
|
402
|
+
@staticmethod
|
|
403
|
+
def __to_server_log_level(log_level):
|
|
404
|
+
"""Convert the given log level to the corresponding server log level.
|
|
405
|
+
|
|
406
|
+
Parameters
|
|
407
|
+
----------
|
|
408
|
+
log_level : str
|
|
409
|
+
Level of logging. Options are "critical" "debug", "error", "info", and "warning".
|
|
410
|
+
|
|
411
|
+
Returns
|
|
412
|
+
-------
|
|
413
|
+
int
|
|
414
|
+
Corresponding server log level.
|
|
415
|
+
"""
|
|
416
|
+
log_level = log_level.lower()
|
|
417
|
+
for level_name, server_level in WorkbenchClient.__log_levels.items():
|
|
418
|
+
if log_level in level_name:
|
|
419
|
+
return server_level[0]
|
|
420
|
+
return wb.LOG_NONE
|
|
421
|
+
|
|
422
|
+
__log_levels = {
|
|
423
|
+
"none null": (wb.LOG_NONE, logging.NOTSET),
|
|
424
|
+
"debug": (wb.LOG_DEBUG, logging.DEBUG),
|
|
425
|
+
"information": (wb.LOG_INFO, logging.INFO),
|
|
426
|
+
"warning": (wb.LOG_WARNING, logging.WARNING),
|
|
427
|
+
"error": (wb.LOG_ERROR, logging.ERROR),
|
|
428
|
+
"fatal critical": (wb.LOG_FATAL, logging.CRITICAL),
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
def start_mechanical_server(self, system_name):
|
|
432
|
+
"""Start the PyMechanical server for the given system in the Workbench project.
|
|
433
|
+
|
|
434
|
+
Parameters
|
|
435
|
+
----------
|
|
436
|
+
system_name : str
|
|
437
|
+
Name of the system in the Workbench project.
|
|
438
|
+
|
|
439
|
+
Returns
|
|
440
|
+
-------
|
|
441
|
+
int
|
|
442
|
+
Port number of the PyMechanical server used to start the PyMechanical client.
|
|
443
|
+
|
|
444
|
+
Examples
|
|
445
|
+
--------
|
|
446
|
+
Start a PyMechanical session for the given system name.
|
|
447
|
+
|
|
448
|
+
>>> from ansys.mechanical.core import launch_mechanical
|
|
449
|
+
>>> server_port=wb.start_mechanical_server(system_name=mech_system_name)
|
|
450
|
+
>>> mechanical = launch_mechanical(start_instance=False, port=server_port)
|
|
451
|
+
|
|
452
|
+
"""
|
|
453
|
+
pymech_port = self.run_script_string(
|
|
454
|
+
f"""import json
|
|
455
|
+
server_port=LaunchMechanicalServerOnSystem(SystemName="{system_name}")
|
|
456
|
+
wb_script_result=json.dumps(server_port)
|
|
457
|
+
"""
|
|
458
|
+
)
|
|
459
|
+
return pymech_port
|
|
460
|
+
|
|
461
|
+
def start_fluent_server(self, system_name):
|
|
462
|
+
"""Start the PyFluent server for the given system in the Workbench project.
|
|
463
|
+
|
|
464
|
+
Parameters
|
|
465
|
+
----------
|
|
466
|
+
system_name : str
|
|
467
|
+
Name of the system in the Workbench project.
|
|
468
|
+
|
|
469
|
+
Returns
|
|
470
|
+
-------
|
|
471
|
+
str
|
|
472
|
+
Path to the local file containing the PyFluent server information that
|
|
473
|
+
can be used to start a PyFluent client.
|
|
474
|
+
|
|
475
|
+
Examples
|
|
476
|
+
--------
|
|
477
|
+
Start a PyFluent session for the given system name.
|
|
478
|
+
|
|
479
|
+
>>> import ansys.fluent.core as pyfluent
|
|
480
|
+
>>> server_info_file=wb.start_fluent_server(system_name=fluent_sys_name)
|
|
481
|
+
>>> fluent=pyfluent.connect_to_fluent(server_info_file_name=server_info_file)
|
|
482
|
+
|
|
483
|
+
"""
|
|
484
|
+
server_info_file_name = self.run_script_string(
|
|
485
|
+
f"""import json
|
|
486
|
+
server_info_file=LaunchFluentServerOnSystem(SystemName="{system_name}")
|
|
487
|
+
wb_script_result=json.dumps(server_info_file)
|
|
488
|
+
"""
|
|
489
|
+
)
|
|
490
|
+
local_copy = os.path.join(self.workdir, server_info_file_name)
|
|
491
|
+
if os.path.exists(local_copy):
|
|
492
|
+
os.remove(local_copy)
|
|
493
|
+
self.download_file(server_info_file_name, show_progress=False)
|
|
494
|
+
return local_copy
|
|
495
|
+
|
|
496
|
+
def start_sherlock_server(self, system_name):
|
|
497
|
+
"""Start the PySherlock server for the given system in the Workbench project.
|
|
498
|
+
|
|
499
|
+
Parameters
|
|
500
|
+
----------
|
|
501
|
+
system_name : str
|
|
502
|
+
Name of the system in the Workbench project.
|
|
503
|
+
|
|
504
|
+
Returns
|
|
505
|
+
-------
|
|
506
|
+
int
|
|
507
|
+
Prt number of the PySherlock server used to start a PySherlock client.
|
|
508
|
+
|
|
509
|
+
Examples
|
|
510
|
+
--------
|
|
511
|
+
Start a PySherlock session for the given system name.
|
|
512
|
+
|
|
513
|
+
>>> from ansys.sherlock.core import pysherlock
|
|
514
|
+
>>> server_port=wb.start_sherlock_server(system_name=sherlock_system_name)
|
|
515
|
+
>>> sherlock = pysherlock.connect_grpc_channel(port=server_port)
|
|
516
|
+
>>> sherlock.common.check()
|
|
517
|
+
|
|
518
|
+
"""
|
|
519
|
+
pysherlock_port = self.run_script_string(
|
|
520
|
+
f"""import json
|
|
521
|
+
server_port=LaunchSherlockServerOnSystem(SystemName="{system_name}")
|
|
522
|
+
wb_script_result=json.dumps(server_port)
|
|
523
|
+
"""
|
|
524
|
+
)
|
|
525
|
+
return pysherlock_port
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
__all__ = ["WorkbenchClient"]
|