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.
@@ -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"]
@@ -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"]