ansys-pyensight-core 0.11.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ansys/pyensight/core/__init__.py +41 -0
- ansys/pyensight/core/common.py +341 -0
- ansys/pyensight/core/deep_pixel_view.html +98 -0
- ansys/pyensight/core/dockerlauncher.py +1124 -0
- ansys/pyensight/core/dvs.py +872 -0
- ansys/pyensight/core/enscontext.py +345 -0
- ansys/pyensight/core/enshell_grpc.py +641 -0
- ansys/pyensight/core/ensight_grpc.py +874 -0
- ansys/pyensight/core/ensobj.py +515 -0
- ansys/pyensight/core/launch_ensight.py +296 -0
- ansys/pyensight/core/launcher.py +388 -0
- ansys/pyensight/core/libuserd.py +2110 -0
- ansys/pyensight/core/listobj.py +280 -0
- ansys/pyensight/core/locallauncher.py +579 -0
- ansys/pyensight/core/py.typed +0 -0
- ansys/pyensight/core/renderable.py +880 -0
- ansys/pyensight/core/session.py +1923 -0
- ansys/pyensight/core/sgeo_poll.html +24 -0
- ansys/pyensight/core/utils/__init__.py +21 -0
- ansys/pyensight/core/utils/adr.py +111 -0
- ansys/pyensight/core/utils/dsg_server.py +1220 -0
- ansys/pyensight/core/utils/export.py +606 -0
- ansys/pyensight/core/utils/omniverse.py +769 -0
- ansys/pyensight/core/utils/omniverse_cli.py +614 -0
- ansys/pyensight/core/utils/omniverse_dsg_server.py +1196 -0
- ansys/pyensight/core/utils/omniverse_glb_server.py +848 -0
- ansys/pyensight/core/utils/parts.py +1221 -0
- ansys/pyensight/core/utils/query.py +487 -0
- ansys/pyensight/core/utils/readers.py +300 -0
- ansys/pyensight/core/utils/resources/Materials/000_sky.exr +0 -0
- ansys/pyensight/core/utils/support.py +128 -0
- ansys/pyensight/core/utils/variables.py +2019 -0
- ansys/pyensight/core/utils/views.py +674 -0
- ansys_pyensight_core-0.11.0.dist-info/METADATA +309 -0
- ansys_pyensight_core-0.11.0.dist-info/RECORD +37 -0
- ansys_pyensight_core-0.11.0.dist-info/WHEEL +4 -0
- ansys_pyensight_core-0.11.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
# Copyright (C) 2022 - 2026 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
|
+
"""launch_ensight module
|
|
24
|
+
|
|
25
|
+
The launch_ensight module provides pyensight with the ability to launch an
|
|
26
|
+
EnSight session using PyPIM. This leverages the DockerLauncher module.
|
|
27
|
+
|
|
28
|
+
Examples
|
|
29
|
+
--------
|
|
30
|
+
>>> from ansys.pyensight.core import launch_ensight
|
|
31
|
+
>>> session = launch_ensight()
|
|
32
|
+
>>> # do pyensight stuff with the session
|
|
33
|
+
>>> session.close()
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
import logging
|
|
37
|
+
from typing import Optional
|
|
38
|
+
|
|
39
|
+
from ansys.pyensight.core.locallauncher import LocalLauncher
|
|
40
|
+
from ansys.pyensight.core.session import Session
|
|
41
|
+
|
|
42
|
+
pim_is_available = False # pragma: no cover
|
|
43
|
+
try:
|
|
44
|
+
import ansys.platform.instancemanagement as pypim
|
|
45
|
+
|
|
46
|
+
pim_is_available = True # pragma: no cover
|
|
47
|
+
except Exception: # pragma: no cover
|
|
48
|
+
pass # pragma: no cover
|
|
49
|
+
logging.debug(f"pim_is_available: {pim_is_available}\n")
|
|
50
|
+
|
|
51
|
+
docker_is_available = False
|
|
52
|
+
try:
|
|
53
|
+
from ansys.pyensight.core.dockerlauncher import DockerLauncher
|
|
54
|
+
|
|
55
|
+
docker_is_available = True
|
|
56
|
+
except Exception: # pragma: no cover
|
|
57
|
+
pass # pragma: no cover
|
|
58
|
+
logging.debug(f"docker_is_available: {docker_is_available}\n")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if pim_is_available: # pragma: no cover
|
|
62
|
+
|
|
63
|
+
def _prepare_pim( # pragma: no cover
|
|
64
|
+
product_version: Optional[str] = None,
|
|
65
|
+
):
|
|
66
|
+
"""Create a PIM instance and gRPC channel for the input version of EnSight.
|
|
67
|
+
|
|
68
|
+
Parameters
|
|
69
|
+
----------
|
|
70
|
+
product_version : str, optional
|
|
71
|
+
Version of the product. For example, "232". The default is "None"
|
|
72
|
+
|
|
73
|
+
"""
|
|
74
|
+
pim = pypim.connect()
|
|
75
|
+
instance = pim.create_instance(
|
|
76
|
+
product_name="ensight",
|
|
77
|
+
product_version=product_version,
|
|
78
|
+
)
|
|
79
|
+
instance.wait_for_ready()
|
|
80
|
+
# use defaults as specified by PIM
|
|
81
|
+
channel = instance.build_grpc_channel(
|
|
82
|
+
options=[
|
|
83
|
+
("grpc.max_receive_message_length", -1),
|
|
84
|
+
("grpc.max_send_message_length", -1),
|
|
85
|
+
("grpc.testing.fixed_reconnect_backoff_ms", 1100),
|
|
86
|
+
]
|
|
87
|
+
)
|
|
88
|
+
return instance, channel
|
|
89
|
+
|
|
90
|
+
def _launch_ensight_with_pim( # pragma: no cover
|
|
91
|
+
product_version: Optional[str] = None,
|
|
92
|
+
**kwargs,
|
|
93
|
+
) -> "Session":
|
|
94
|
+
"""Internal function.
|
|
95
|
+
Start via PyPIM the EnSight Docker container with EnShell as the ENTRYPOINT.
|
|
96
|
+
Create and bind a Session instance to the created gRPC session. Return that session.
|
|
97
|
+
|
|
98
|
+
Parameters
|
|
99
|
+
----------
|
|
100
|
+
product_version : str, optional
|
|
101
|
+
Version of the product. For example, "232". The default is "None"
|
|
102
|
+
use_egl : bool, optional
|
|
103
|
+
If True, EGL hardware accelerated graphics will be used. The platform
|
|
104
|
+
must be able to support it.
|
|
105
|
+
use_sos : int, optional
|
|
106
|
+
If None, don't use SOS. Otherwise, it's the number of EnSight Servers to use (int).
|
|
107
|
+
|
|
108
|
+
Returns
|
|
109
|
+
-------
|
|
110
|
+
Session
|
|
111
|
+
pyensight Session object instance
|
|
112
|
+
|
|
113
|
+
"""
|
|
114
|
+
instance, channel = _prepare_pim(product_version=product_version)
|
|
115
|
+
launcher = DockerLauncher(channel=channel, pim_instance=instance, **kwargs)
|
|
116
|
+
return launcher.connect()
|
|
117
|
+
|
|
118
|
+
def _launch_libuserd_with_pim(
|
|
119
|
+
product_version: Optional[str] = None, **kwargs
|
|
120
|
+
): # pragma: no cover
|
|
121
|
+
from ansys.pyensight.core.libuserd import LibUserd
|
|
122
|
+
|
|
123
|
+
instance, channel = _prepare_pim(product_version=product_version)
|
|
124
|
+
libuserd = LibUserd(channel=channel, pim_instance=instance, **kwargs)
|
|
125
|
+
libuserd.initialize()
|
|
126
|
+
return libuserd
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def launch_ensight(
|
|
130
|
+
product_version: Optional[str] = None,
|
|
131
|
+
use_pim: bool = True,
|
|
132
|
+
use_docker: bool = True,
|
|
133
|
+
data_directory: Optional[str] = None,
|
|
134
|
+
docker_image_name: Optional[str] = None,
|
|
135
|
+
use_dev: bool = False,
|
|
136
|
+
ansys_installation: Optional[str] = None,
|
|
137
|
+
application: str = "ensight",
|
|
138
|
+
batch: bool = True,
|
|
139
|
+
**kwargs,
|
|
140
|
+
) -> "Session":
|
|
141
|
+
"""Start an EnSight session via EnShell using the Docker EnSight Image.
|
|
142
|
+
Return that session.
|
|
143
|
+
|
|
144
|
+
Parameters
|
|
145
|
+
----------
|
|
146
|
+
product_version : str, optional
|
|
147
|
+
Select an installed version of ANSYS. The string must be in a format like
|
|
148
|
+
"232" (for 2023 R2). The default is "None", in which case the newest installed
|
|
149
|
+
version is used.
|
|
150
|
+
use_pim : bool, optional
|
|
151
|
+
If True, then PyPIM is used to launch EnSight.
|
|
152
|
+
use_docker : bool, optional
|
|
153
|
+
If True, use DockerLaucher. If use_pim is True, this option is ignored.
|
|
154
|
+
data_directory: str, optional
|
|
155
|
+
Host directory to make into the Docker container at /data
|
|
156
|
+
Only used if use_docker is True.
|
|
157
|
+
docker_image_name: str, optional
|
|
158
|
+
Optional Docker Image name to use
|
|
159
|
+
use_dev: bool, optional
|
|
160
|
+
Option to use the latest ensight_dev Docker Image; overridden by docker_image_name if specified.
|
|
161
|
+
ansys_installation: str, optional
|
|
162
|
+
Location of the ANSYS installation, including the version.
|
|
163
|
+
directory Default: None (causes common locations to be scanned).
|
|
164
|
+
If use_pim is True, this option is ignored. If use_docker is True, this option is ignored.
|
|
165
|
+
application: str, optional
|
|
166
|
+
The application to be launched. By default, "ensight", but
|
|
167
|
+
"envision" is also available.
|
|
168
|
+
batch: bool, optional
|
|
169
|
+
By default, the EnSight/EnVision instance will run in batch mode.
|
|
170
|
+
If batch is set to False, the full GUI will be presented.
|
|
171
|
+
Only used if use_pim and use_docker are False.
|
|
172
|
+
use_egl: bool, optional
|
|
173
|
+
If True, EGL hardware accelerated graphics will be used. The platform
|
|
174
|
+
must be able to support it.
|
|
175
|
+
use_sos: int, optional
|
|
176
|
+
If None, don't use SOS. Otherwise, it's the number of EnSight Servers to use (int).
|
|
177
|
+
timeout: float, optional
|
|
178
|
+
In some cases where the EnSight session can take a significant amount of
|
|
179
|
+
time to start up, this is the number of seconds to wait before failing
|
|
180
|
+
the connection. The default is 120.0.
|
|
181
|
+
|
|
182
|
+
Returns
|
|
183
|
+
-------
|
|
184
|
+
type
|
|
185
|
+
pyensight Session object instance
|
|
186
|
+
|
|
187
|
+
Raises
|
|
188
|
+
------
|
|
189
|
+
RuntimeError
|
|
190
|
+
variety of error conditions
|
|
191
|
+
|
|
192
|
+
"""
|
|
193
|
+
|
|
194
|
+
logging.debug(f"pim_is_available: {pim_is_available} use_pim: {use_pim}\n") # pragma: no cover
|
|
195
|
+
if pim_is_available and use_pim: # pragma: no cover
|
|
196
|
+
if pypim.is_configured():
|
|
197
|
+
return _launch_ensight_with_pim(product_version=product_version, **kwargs)
|
|
198
|
+
|
|
199
|
+
# not using PIM, but use Docker
|
|
200
|
+
logging.debug(f"docker_is_available: {docker_is_available} use_docker: {use_docker}\n")
|
|
201
|
+
if docker_is_available and use_docker:
|
|
202
|
+
launcher = DockerLauncher(
|
|
203
|
+
data_directory=data_directory,
|
|
204
|
+
docker_image_name=docker_image_name,
|
|
205
|
+
use_dev=use_dev,
|
|
206
|
+
**kwargs,
|
|
207
|
+
)
|
|
208
|
+
return launcher.start()
|
|
209
|
+
|
|
210
|
+
# use local installation of EnSight
|
|
211
|
+
launcher = LocalLauncher( # pragma: no cover
|
|
212
|
+
ansys_installation=ansys_installation, # pragma: no cover
|
|
213
|
+
application=application, # pragma: no cover
|
|
214
|
+
batch=batch, # pragma: no cover
|
|
215
|
+
**kwargs, # pragma: no cover
|
|
216
|
+
) # pragma: no cover
|
|
217
|
+
return launcher.start() # pragma: no cover
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def launch_libuserd( # pragma: no cover
|
|
221
|
+
product_version: Optional[str] = None,
|
|
222
|
+
use_pim: bool = True,
|
|
223
|
+
use_docker: bool = True,
|
|
224
|
+
data_directory: Optional[str] = None,
|
|
225
|
+
docker_image_name: Optional[str] = None,
|
|
226
|
+
use_dev: bool = False,
|
|
227
|
+
ansys_installation: Optional[str] = None,
|
|
228
|
+
timeout: float = 120.0,
|
|
229
|
+
pull_image_if_not_available: bool = False,
|
|
230
|
+
):
|
|
231
|
+
"""Start an EnSight session via EnShell using the Docker EnSight Image.
|
|
232
|
+
Return that session.
|
|
233
|
+
|
|
234
|
+
Parameters
|
|
235
|
+
----------
|
|
236
|
+
product_version : str, optional
|
|
237
|
+
Select an installed version of ANSYS. The string must be in a format like
|
|
238
|
+
"232" (for 2023 R2). The default is "None", in which case the newest installed
|
|
239
|
+
version is used.
|
|
240
|
+
use_pim : bool, optional
|
|
241
|
+
If True, then PyPIM is used to launch the EnSight image.
|
|
242
|
+
use_docker : bool, optional
|
|
243
|
+
If True, use DockerLaucher. If use_pim is True, this option is ignored.
|
|
244
|
+
data_directory: str, optional
|
|
245
|
+
Host directory to make into the Docker container at /data
|
|
246
|
+
Only used if use_docker is True.
|
|
247
|
+
docker_image_name: str, optional
|
|
248
|
+
Optional Docker Image name to use
|
|
249
|
+
use_dev: bool, optional
|
|
250
|
+
Option to use the latest ensight_dev Docker Image; overridden by docker_image_name if specified.
|
|
251
|
+
ansys_installation: str, optional
|
|
252
|
+
Location of the ANSYS installation, including the version.
|
|
253
|
+
directory Default: None (causes common locations to be scanned).
|
|
254
|
+
If use_pim is True, this option is ignored. If use_docker is True, this option is ignored.
|
|
255
|
+
application: str, optional
|
|
256
|
+
The application to be launched. By default, "ensight", but
|
|
257
|
+
"envision" is also available.
|
|
258
|
+
timeout: float, optional
|
|
259
|
+
In some cases where the EnSight session can take a significant amount of
|
|
260
|
+
time to start up, this is the number of seconds to wait before failing
|
|
261
|
+
the connection. The default is 120.0.
|
|
262
|
+
pull_image_if_not_available: bool
|
|
263
|
+
If True, the image will be pulled using Docker. If use_pim is True this option
|
|
264
|
+
is ignored.
|
|
265
|
+
Returns
|
|
266
|
+
-------
|
|
267
|
+
type
|
|
268
|
+
LibUserd object instance
|
|
269
|
+
|
|
270
|
+
Raises
|
|
271
|
+
------
|
|
272
|
+
RuntimeError
|
|
273
|
+
variety of error conditions
|
|
274
|
+
|
|
275
|
+
"""
|
|
276
|
+
from ansys.pyensight.core.libuserd import LibUserd
|
|
277
|
+
|
|
278
|
+
logging.debug(f"pim_is_available: {pim_is_available} use_pim: {use_pim}\n") # pragma: no cover
|
|
279
|
+
if pim_is_available and use_pim: # pragma: no cover
|
|
280
|
+
if pypim.is_configured():
|
|
281
|
+
return _launch_libuserd_with_pim(product_version=product_version, timeout=timeout)
|
|
282
|
+
logging.debug(f"docker_is_available: {docker_is_available} use_docker: {use_docker}\n")
|
|
283
|
+
if docker_is_available and use_docker:
|
|
284
|
+
libuserd = LibUserd(
|
|
285
|
+
data_directory=data_directory,
|
|
286
|
+
docker_image_name=docker_image_name,
|
|
287
|
+
use_dev=use_dev,
|
|
288
|
+
use_docker=use_docker,
|
|
289
|
+
timeout=timeout,
|
|
290
|
+
pull_image_if_not_available=pull_image_if_not_available,
|
|
291
|
+
)
|
|
292
|
+
libuserd.initialize()
|
|
293
|
+
return libuserd
|
|
294
|
+
libuserd = LibUserd(ansys_installation=ansys_installation, timeout=timeout)
|
|
295
|
+
libuserd.initialize()
|
|
296
|
+
return libuserd
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
# Copyright (C) 2022 - 2026 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
|
+
"""Launcher module.
|
|
24
|
+
|
|
25
|
+
The Launcher module provides a base class responsible for managing an EnSight
|
|
26
|
+
:class:`Session<ansys.pyensight.core.Session>` instance. Subclasses of the
|
|
27
|
+
class implement specific launching paradigms.
|
|
28
|
+
|
|
29
|
+
Examples:
|
|
30
|
+
::
|
|
31
|
+
|
|
32
|
+
from ansys.pyensight.core import LocalLauncher
|
|
33
|
+
session = LocalLauncher().start()
|
|
34
|
+
|
|
35
|
+
"""
|
|
36
|
+
import os.path
|
|
37
|
+
import platform
|
|
38
|
+
import re
|
|
39
|
+
from typing import TYPE_CHECKING, Dict, List, Optional
|
|
40
|
+
import warnings
|
|
41
|
+
|
|
42
|
+
import psutil
|
|
43
|
+
import requests
|
|
44
|
+
|
|
45
|
+
if TYPE_CHECKING:
|
|
46
|
+
from ansys.pyensight.core import Session
|
|
47
|
+
|
|
48
|
+
# Don't remove this line. The idna encoding
|
|
49
|
+
# is used by getaddrinfo when dealing with unicode hostnames,
|
|
50
|
+
# and in some cases, there appears to be a race condition
|
|
51
|
+
# where threads will get a LookupError on getaddrinfo() saying
|
|
52
|
+
# that the encoding doesn't exist. Using the idna encoding before
|
|
53
|
+
# running any CLI code (and any threads it may create) ensures that
|
|
54
|
+
# the encodings.idna is imported and registered in the codecs registry,
|
|
55
|
+
# which will stop the LookupErrors from happening.
|
|
56
|
+
# See: https://bugs.python.org/issue29288
|
|
57
|
+
"".encode("idna")
|
|
58
|
+
|
|
59
|
+
# The user doesn't know "eth" and "ib" what they mean. Use more meaningful
|
|
60
|
+
# keywords.
|
|
61
|
+
INTERCONNECT_MAP = {"ethernet": "eth", "infiniband": "ib"}
|
|
62
|
+
|
|
63
|
+
MPI_TYPES = ["intel2018", "intel2021", "openmpi"]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class Launcher:
|
|
67
|
+
"""Provides the EnSight ``Launcher`` base class.
|
|
68
|
+
|
|
69
|
+
A ``Launcher`` instance is used to start and end an EnSight session.
|
|
70
|
+
Specific subclasses handle different types of launching semantics.
|
|
71
|
+
A launcher can create only a single EnSight instance. If you need to
|
|
72
|
+
have more than one EnSight instance, a new launcher instance is required.
|
|
73
|
+
|
|
74
|
+
Parameters
|
|
75
|
+
----------
|
|
76
|
+
timeout : float, optional
|
|
77
|
+
Number of seconds to try a gRPC connection before giving up.
|
|
78
|
+
The default is ``120``.
|
|
79
|
+
use_egl : bool, optional
|
|
80
|
+
Whether to use EGL hardware for accelerated graphics. The platform
|
|
81
|
+
must be able to support this hardware. The default is ``False``.
|
|
82
|
+
use_sos : int, optional
|
|
83
|
+
Number of EnSight servers to use for SOS (Server of Server) mode.
|
|
84
|
+
The default is ``None``, in which case SOS mode is not used.
|
|
85
|
+
enable_rest_api : bool, optional
|
|
86
|
+
Whether to enable the EnSight REST API. The default is ``False``.
|
|
87
|
+
This parameter is supported in EnSight 2024 R1 and later.
|
|
88
|
+
additional_command_line_options: list, optional
|
|
89
|
+
Additional command line options to be used to launch EnSight.
|
|
90
|
+
Please note, when using DockerLauncher, arguments that contain spaces
|
|
91
|
+
are not supported.
|
|
92
|
+
launch_web_ui : bool, optional
|
|
93
|
+
Whether to launch the webUI from EnSight
|
|
94
|
+
use_mpi: str, optional
|
|
95
|
+
If set, EnSight will be launched with the MPI type selected. The valid
|
|
96
|
+
values depend on the EnSight version to be used. The user can see
|
|
97
|
+
the specific list starting the EnSight Launcher manually and specifying the options
|
|
98
|
+
to launch EnSight in parallel and MPI. Here are reported the values for releases
|
|
99
|
+
2024R2 and 2025R1.
|
|
100
|
+
|
|
101
|
+
=================== =========================================
|
|
102
|
+
Release Valid MPI Types
|
|
103
|
+
=================== =========================================
|
|
104
|
+
2024R2 intel2021, intel2018, openmpi
|
|
105
|
+
2025R1 intel2021, intel2018, openmpi
|
|
106
|
+
=================== =========================================
|
|
107
|
+
|
|
108
|
+
The remote nodes must be Linux nodes.
|
|
109
|
+
This option is valid only if a LocalLauncher is used.
|
|
110
|
+
interconnet: str, optional
|
|
111
|
+
If set, EnSight will be launched with the MPI Interconnect selected. Valid values
|
|
112
|
+
are "ethernet", "infiniband". It requires use_mpi to be set.
|
|
113
|
+
If use_mpi is set and interconnect is not, "ethernet" will be used.
|
|
114
|
+
This option is valid only if a LocalLauncher is used.
|
|
115
|
+
server_hosts: List[str], optional
|
|
116
|
+
A list of hostnames where the server processes should be spawned on when MPI is selected.
|
|
117
|
+
If use_mpi is set and server_hosts not, it will default to "localhost".
|
|
118
|
+
This option is valid only if a LocalLauncher is used.
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
def __init__(
|
|
122
|
+
self,
|
|
123
|
+
timeout: float = 120.0,
|
|
124
|
+
use_egl: bool = False,
|
|
125
|
+
use_sos: Optional[int] = None,
|
|
126
|
+
enable_rest_api: bool = False,
|
|
127
|
+
additional_command_line_options: Optional[List] = None,
|
|
128
|
+
launch_webui: bool = False,
|
|
129
|
+
use_mpi: Optional[str] = None,
|
|
130
|
+
interconnect: Optional[str] = None,
|
|
131
|
+
server_hosts: Optional[List[str]] = None,
|
|
132
|
+
rest_ws_separate_loops: bool = False,
|
|
133
|
+
do_not_start_ws: bool = False,
|
|
134
|
+
liben_rest: bool = False,
|
|
135
|
+
vtk_ws: bool = False,
|
|
136
|
+
) -> None:
|
|
137
|
+
self._timeout = timeout
|
|
138
|
+
self._use_egl_param_val: bool = use_egl
|
|
139
|
+
self._use_sos = use_sos
|
|
140
|
+
self._use_mpi = use_mpi
|
|
141
|
+
self._interconnect = interconnect
|
|
142
|
+
self._vtk_ws_port = vtk_ws
|
|
143
|
+
if self._use_mpi and self._use_mpi not in MPI_TYPES:
|
|
144
|
+
raise RuntimeError(f"{self._use_mpi} is not a valid MPI option.")
|
|
145
|
+
if self._use_mpi and not self._interconnect:
|
|
146
|
+
self._interconnect = "ethernet"
|
|
147
|
+
if self._interconnect:
|
|
148
|
+
if self._interconnect not in list(INTERCONNECT_MAP.values()):
|
|
149
|
+
raise RuntimeError(f"{self._interconnect} is not a valid MPI interconnect option.")
|
|
150
|
+
self._interconnect = INTERCONNECT_MAP.get(self._interconnect)
|
|
151
|
+
self._server_hosts = server_hosts
|
|
152
|
+
if self._use_mpi and not self._server_hosts:
|
|
153
|
+
self._server_hosts = ["localhost"]
|
|
154
|
+
self._enable_rest_api = enable_rest_api
|
|
155
|
+
|
|
156
|
+
self._sessions: List[Session] = []
|
|
157
|
+
self._session_directory: str = "."
|
|
158
|
+
|
|
159
|
+
self._is_egl_capable: Optional[bool] = None
|
|
160
|
+
self._egl_env_val: Optional[bool] = None
|
|
161
|
+
egl_env = os.environ.get("PYENSIGHT_FORCE_ENSIGHT_EGL")
|
|
162
|
+
if egl_env is not None:
|
|
163
|
+
if egl_env == "1": # pragma: no cover
|
|
164
|
+
self._egl_env_val = True # pragma: no cover
|
|
165
|
+
else:
|
|
166
|
+
self._egl_env_val = False
|
|
167
|
+
# a dict of any optional launcher specific query parameters for URLs
|
|
168
|
+
self._query_parameters: Dict[str, str] = {}
|
|
169
|
+
self._additional_command_line_options = additional_command_line_options
|
|
170
|
+
self._launch_webui = launch_webui
|
|
171
|
+
self._do_not_start_ws = do_not_start_ws
|
|
172
|
+
self._liben_rest = liben_rest
|
|
173
|
+
self._rest_ws_separate_loops = rest_ws_separate_loops
|
|
174
|
+
self._has_grpc_changes = False
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def session_directory(self) -> str:
|
|
178
|
+
"""Root directory for HTML files.
|
|
179
|
+
|
|
180
|
+
The contents of this directory can be accessed at ``hostname:port``.
|
|
181
|
+
"""
|
|
182
|
+
return self._session_directory
|
|
183
|
+
|
|
184
|
+
@session_directory.setter
|
|
185
|
+
def session_directory(self, value: str):
|
|
186
|
+
self._session_directory = value
|
|
187
|
+
|
|
188
|
+
def close(self, session: "Session") -> None:
|
|
189
|
+
"""Shut down the launched EnSight session.
|
|
190
|
+
|
|
191
|
+
This method closes all associated sessions and then stops the
|
|
192
|
+
launched EnSight instance.
|
|
193
|
+
|
|
194
|
+
Parameters
|
|
195
|
+
----------
|
|
196
|
+
session : ``pyensight.Session``
|
|
197
|
+
Session to close.
|
|
198
|
+
|
|
199
|
+
Raises
|
|
200
|
+
------
|
|
201
|
+
RuntimeError
|
|
202
|
+
If the session was not launched by this launcher.
|
|
203
|
+
|
|
204
|
+
"""
|
|
205
|
+
if session not in self._sessions:
|
|
206
|
+
raise RuntimeError("Session not associated with this Launcher")
|
|
207
|
+
self._sessions.remove(session)
|
|
208
|
+
if self._sessions:
|
|
209
|
+
# stop the grpc session interface
|
|
210
|
+
session.grpc.shutdown(stop_ensight=False)
|
|
211
|
+
return
|
|
212
|
+
|
|
213
|
+
# if the session list is empty, stop the EnSight instance (via session grpc interface)
|
|
214
|
+
session.grpc.shutdown(stop_ensight=True, force=True)
|
|
215
|
+
|
|
216
|
+
# stop the websocketserver instance
|
|
217
|
+
url = f"http://{session.hostname}:{session.html_port}/v1/stop"
|
|
218
|
+
if session.secret_key: # pragma: no cover
|
|
219
|
+
url += f"?security_token={session.secret_key}"
|
|
220
|
+
try:
|
|
221
|
+
_ = requests.get(url)
|
|
222
|
+
except requests.exceptions.ConnectionError:
|
|
223
|
+
pass
|
|
224
|
+
|
|
225
|
+
# Stop the launcher instance
|
|
226
|
+
self.stop()
|
|
227
|
+
|
|
228
|
+
def start(self) -> Optional["Session"]:
|
|
229
|
+
"""Start a session using the current launcher
|
|
230
|
+
|
|
231
|
+
The start() method will only allocate a single instance of
|
|
232
|
+
a Session object. If called a second time, return the
|
|
233
|
+
result of the first call.
|
|
234
|
+
|
|
235
|
+
Returns
|
|
236
|
+
-------
|
|
237
|
+
If start() has been called previously, return that session
|
|
238
|
+
and emit a warning. If start() has not been called, return None.
|
|
239
|
+
|
|
240
|
+
"""
|
|
241
|
+
if len(self._sessions):
|
|
242
|
+
msg = "The launcher start() method may only be called once. "
|
|
243
|
+
msg += "Create a new launcher instance to start a new EnSight instance."
|
|
244
|
+
warnings.warn(msg, RuntimeWarning)
|
|
245
|
+
return self._sessions[0]
|
|
246
|
+
return None
|
|
247
|
+
|
|
248
|
+
def stop(self) -> None:
|
|
249
|
+
"""Base method for stopping a session initiated by start()
|
|
250
|
+
|
|
251
|
+
Notes
|
|
252
|
+
-----
|
|
253
|
+
The session object is responsible for making the EnSight 'Exit' and websocketserver
|
|
254
|
+
calls. This method can be used to clean up any additional resources being used
|
|
255
|
+
by the launching method.
|
|
256
|
+
"""
|
|
257
|
+
return
|
|
258
|
+
|
|
259
|
+
def _find_ports_used_by_other_pyensight_and_ensight(self):
|
|
260
|
+
"""Find ports to avoid when looking for empty ports.
|
|
261
|
+
|
|
262
|
+
The ports are found iterating the current processes and
|
|
263
|
+
looking for PyEnSight/EnSight sessions and their command
|
|
264
|
+
lines.
|
|
265
|
+
"""
|
|
266
|
+
pyensight_found = []
|
|
267
|
+
ensight_found = []
|
|
268
|
+
for process in psutil.process_iter():
|
|
269
|
+
try:
|
|
270
|
+
process_cmdline = process.cmdline()
|
|
271
|
+
except (psutil.AccessDenied, psutil.ZombieProcess, OSError, psutil.NoSuchProcess):
|
|
272
|
+
continue
|
|
273
|
+
if not process_cmdline:
|
|
274
|
+
continue
|
|
275
|
+
if len(process_cmdline) > 1:
|
|
276
|
+
if "websocketserver.py" in os.path.basename(process_cmdline[1]):
|
|
277
|
+
pyensight_found.append(process_cmdline)
|
|
278
|
+
if any(["ensight" in os.path.basename(x) for x in process_cmdline]):
|
|
279
|
+
if any([x == "-ports" for x in process_cmdline]):
|
|
280
|
+
ensight_found.append(process_cmdline)
|
|
281
|
+
ports = []
|
|
282
|
+
for command_line in pyensight_found:
|
|
283
|
+
for command in command_line:
|
|
284
|
+
if re.match(r"^\d{4,5}$", command):
|
|
285
|
+
ports.append(int(command))
|
|
286
|
+
for command_line in ensight_found:
|
|
287
|
+
idx = command_line.index("-ports") + 1
|
|
288
|
+
ports.append(int(command_line[idx]))
|
|
289
|
+
return list(set(ports))
|
|
290
|
+
|
|
291
|
+
def _use_egl(self) -> bool:
|
|
292
|
+
"""Return True if the system supports the EGL and if EGL was desired.
|
|
293
|
+
|
|
294
|
+
Returns
|
|
295
|
+
-------
|
|
296
|
+
bool
|
|
297
|
+
A bool value that is True if we should use EGL.
|
|
298
|
+
|
|
299
|
+
"""
|
|
300
|
+
if self._is_egl_capable is None:
|
|
301
|
+
# if we haven't checked with the subclasss if the system can do EGL
|
|
302
|
+
self._is_egl_capable = self._is_system_egl_capable()
|
|
303
|
+
|
|
304
|
+
if self._is_egl_capable is False:
|
|
305
|
+
# if the system can't do it, return False now
|
|
306
|
+
return False
|
|
307
|
+
|
|
308
|
+
if self._egl_env_val is not None: # pragma: no cover
|
|
309
|
+
# if the environment variable was set, that overrides the constructor option
|
|
310
|
+
return self._egl_env_val
|
|
311
|
+
|
|
312
|
+
# otherwise, use the arg passed to the constructor
|
|
313
|
+
return self._use_egl_param_val # pragma: no cover
|
|
314
|
+
|
|
315
|
+
def _is_system_egl_capable(self) -> bool: # pragma: no cover
|
|
316
|
+
"""Return True if the system supports the EGL launch.
|
|
317
|
+
|
|
318
|
+
Returns
|
|
319
|
+
-------
|
|
320
|
+
bool
|
|
321
|
+
A bool value that is True if the system supports the EGL launch.
|
|
322
|
+
|
|
323
|
+
"""
|
|
324
|
+
raise RuntimeError("Unsupported method for this configuration")
|
|
325
|
+
|
|
326
|
+
def _is_windows(self) -> bool:
|
|
327
|
+
"""Return True if it is Windows
|
|
328
|
+
|
|
329
|
+
Returns
|
|
330
|
+
-------
|
|
331
|
+
bool
|
|
332
|
+
a bool that is True if the platform is Windows
|
|
333
|
+
|
|
334
|
+
"""
|
|
335
|
+
return platform.system() == "Windows"
|
|
336
|
+
|
|
337
|
+
def _get_query_parameters(self) -> Dict[str, str]:
|
|
338
|
+
"""Return optional http query parameters as a dict.
|
|
339
|
+
It may be empty if there are None.
|
|
340
|
+
If query parameters exist, they should be added to any
|
|
341
|
+
http/https URL intended for the WSS web server.
|
|
342
|
+
This is used by things such as Ansys Lab.
|
|
343
|
+
|
|
344
|
+
Returns
|
|
345
|
+
-------
|
|
346
|
+
dict
|
|
347
|
+
query parameters that should be appended to any queries
|
|
348
|
+
"""
|
|
349
|
+
return self._query_parameters
|
|
350
|
+
|
|
351
|
+
def _add_query_parameters(self, params: Dict[str, str]) -> None:
|
|
352
|
+
"""Add query parameters supplied by params to the
|
|
353
|
+
overall dict of query parameters.
|
|
354
|
+
|
|
355
|
+
Parameters
|
|
356
|
+
----------
|
|
357
|
+
params: dict :
|
|
358
|
+
query parameters to add to overall dict
|
|
359
|
+
"""
|
|
360
|
+
for item, value in params.items(): # pragma: no cover
|
|
361
|
+
self._query_parameters[item] = value # pragma: no cover
|
|
362
|
+
|
|
363
|
+
def _delete_query_parameters(self, params: List[str]) -> None:
|
|
364
|
+
"""Delete query parameters supplied by params from the
|
|
365
|
+
overall dict of query parameters.
|
|
366
|
+
|
|
367
|
+
Parameters
|
|
368
|
+
----------
|
|
369
|
+
params: list :
|
|
370
|
+
query parameters to delete from the overall dict
|
|
371
|
+
"""
|
|
372
|
+
for item in params: # pragma: no cover
|
|
373
|
+
try: # pragma: no cover
|
|
374
|
+
del self._query_parameters[item] # pragma: no cover
|
|
375
|
+
except Exception: # pragma: no cover
|
|
376
|
+
pass # pragma: no cover
|
|
377
|
+
|
|
378
|
+
def _get_versionfrom_buildinfo(self, buildinfo):
|
|
379
|
+
"""Check if the gRPC security options apply to the EnSight install."""
|
|
380
|
+
version_match = re.search("Version: (.*)\n", buildinfo)
|
|
381
|
+
internal_version_match = re.search("Internal: (.*)\n", buildinfo)
|
|
382
|
+
if not internal_version_match:
|
|
383
|
+
raise RuntimeError("Couldn't parse EnSight internal version in BUILDINFO file.")
|
|
384
|
+
internal_version = internal_version_match.group(1)
|
|
385
|
+
if not version_match:
|
|
386
|
+
raise RuntimeError("Couldn't parse EnSight version in BUILDINFO file.")
|
|
387
|
+
ensight_full_version = version_match.group(1)
|
|
388
|
+
return internal_version, ensight_full_version
|