Skip to content

Commit cc9ab8e

Browse files
StefanThoeneSMoraisAnsyspyansys-ci-botpre-commit-ci[bot]pluAtAnsys
authored
feat: switch to ansys tools and decouple requirements (#532)
## Description Comply with [#901](ansys/pyansys#902) Switch from native pyvista to ansys-tools-vizualization-interface only basic implementation done additional steps will be coming ## Issue linked #531 ## Checklist - [x] I have tested my changes locally. - [x] I have added necessary documentation or updated existing documentation. - [x] I have followed the coding style guidelines of this project. - [x] I have added appropriate tests (unit, integration, system). - [x] I have reviewed my changes before submitting this pull request. - [x] I have linked the issue or issues that are solved by the PR if any. - [x] I have assigned this PR to myself. - [x] I have made sure that the title of my PR follows [Conventional commits style](https://www.conventionalcommits.org/en/v1.0.0/#summary) (e.g. ``feat: add optical property``) - [x] I have agreed with the Contributor License Agreement ([CLA](https://developer.ansys.com/form/cla-acceptance)). --------- Co-authored-by: Sebastien Morais <[email protected]> Co-authored-by: pyansys-ci-bot <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Sébastien Morais <[email protected]> Co-authored-by: Pengyuan LU <[email protected]>
1 parent 99bb942 commit cc9ab8e

File tree

5 files changed

+106
-16
lines changed

5 files changed

+106
-16
lines changed

doc/changelog.d/532.added.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
switch to ansys tools and decouple requirements

pyproject.toml

+8-1
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,18 @@ dependencies=[
3232
"grpcio-health-checking>=1.45.0,<1.68",
3333
"ansys-api-speos==0.14.2",
3434
"numpy>=1.20.3,<3",
35-
"pyvista>=0.40.0,<0.45",
3635
"comtypes>=1.4,<1.5",
3736
]
3837

3938
[project.optional-dependencies]
39+
graphics = [
40+
"pyvista>=0.40.0,<0.45",
41+
"ansys-tools-visualization-interface>=0.8.3",
42+
]
4043
tests = [
4144
"pytest==8.3.5",
45+
"pyvista>=0.40.0,<0.45",
46+
"ansys-tools-visualization-interface>=0.8.3",
4247
"ansys-platform-instancemanagement>=1.0.3",
4348
"pytest-cov==6.0.0",
4449
]
@@ -47,6 +52,7 @@ jupyter = [
4752
"jupyterlab>=3",
4853
"ipywidgets",
4954
"pyvista[jupyter]>=0.43,<0.45",
55+
"ansys-tools-visualization-interface>=0.8.3",
5056
"notebook==7.3.3",
5157
]
5258
doc = [
@@ -65,6 +71,7 @@ doc = [
6571
"jupyter-server==2.15.0",
6672
"nbconvert==7.16.6",
6773
"pyvista[jupyter]>=0.43,<0.45",
74+
"ansys-tools-visualization-interface>=0.8.3",
6875
]
6976

7077
[project.urls]

src/ansys/speos/core/generic/general_methods.py

+46
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@
2828
from functools import wraps
2929
import warnings
3030

31+
__GRAPHICS_AVAILABLE = None
32+
GRAPHICS_ERROR = (
33+
"Preview unsupported without 'ansys-tools-visualization_interface' installed. "
34+
"You can install this using `pip install ansys-speos-core[graphics]`."
35+
)
36+
3137

3238
def deprecate_kwargs(old_arguments: dict, removed_version="0.3.0"):
3339
"""Issues deprecation warnings for arguments.
@@ -62,3 +68,43 @@ def wrapper(*args, **kwargs):
6268
return wrapper
6369

6470
return decorator
71+
72+
73+
def run_if_graphics_required(warning=False):
74+
"""Check if graphics are available."""
75+
global __GRAPHICS_AVAILABLE
76+
if __GRAPHICS_AVAILABLE is None:
77+
try:
78+
import pyvista as pv # noqa: F401
79+
80+
from ansys.tools.visualization_interface import Plotter # noqa: F401
81+
82+
__GRAPHICS_AVAILABLE = True
83+
except ImportError: # pragma: no cover
84+
__GRAPHICS_AVAILABLE = False
85+
86+
if __GRAPHICS_AVAILABLE is False and warning is False: # pragma: no cover
87+
raise ImportError(GRAPHICS_ERROR)
88+
elif __GRAPHICS_AVAILABLE is False: # pragma: no cover
89+
warnings.warn(GRAPHICS_ERROR)
90+
91+
92+
def graphics_required(method):
93+
"""Decorate a method as requiring graphics.
94+
95+
Parameters
96+
----------
97+
method : callable
98+
Method to decorate.
99+
100+
Returns
101+
-------
102+
callable
103+
Decorated method.
104+
"""
105+
106+
def wrapper(*args, **kwargs):
107+
run_if_graphics_required()
108+
return method(*args, **kwargs)
109+
110+
return wrapper

src/ansys/speos/core/lxp.py

+24-9
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,22 @@
3030

3131
import os
3232
from pathlib import Path
33-
from typing import Optional, Union
34-
35-
import pyvista as pv
33+
from typing import TYPE_CHECKING, Optional, Union
3634

3735
import ansys.api.speos.lpf.v2.lpf_file_reader_pb2 as lpf_file_reader__v2__pb2
3836
import ansys.api.speos.lpf.v2.lpf_file_reader_pb2_grpc as lpf_file_reader__v2__pb2_grpc
37+
from ansys.speos.core.generic.general_methods import graphics_required
3938
from ansys.speos.core.project import Project, Speos
4039

40+
if TYPE_CHECKING: # pragma: no cover
41+
from ansys.tools.visualization_interface import Plotter
42+
try:
43+
from ansys.speos.core.generic.general_methods import run_if_graphics_required
44+
45+
run_if_graphics_required(warning=True)
46+
except ImportError as err: # pragma: no cover
47+
raise err
48+
4149
ERROR_IDS = [7, 8, 9, 10, 11, 12, 13, 14, 15]
4250
"""Intersection types indicating an error state."""
4351

@@ -420,18 +428,21 @@ def remove_error_rays(self) -> LightPathFinder:
420428
return self
421429

422430
@staticmethod
423-
def __add_ray_to_pv(plotter: pv.Plotter, ray: RayPath, max_ray_length: float):
431+
@graphics_required
432+
def __add_ray_to_pv(plotter: Plotter, ray: RayPath, max_ray_length: float):
424433
"""Add a ray to pyvista plotter.
425434
426435
Parameters
427436
----------
428-
plotter : pv.Plotter
429-
Pyvista plotter object to which rays should be added.
437+
plotter : Plotter
438+
Ansys plotter object to which rays should be added.
430439
ray : script.RayPath
431440
RayPath object which contains ray information to be added.
432441
max_ray_length : float
433442
Length of the last ray.
434443
"""
444+
import pyvista as pv
445+
435446
temp = ray.impacts.copy()
436447
if not 7 <= ray.intersection_type[-1] <= 15:
437448
temp.append(
@@ -445,8 +456,9 @@ def __add_ray_to_pv(plotter: pv.Plotter, ray: RayPath, max_ray_length: float):
445456
mesh = pv.MultipleLines(temp)
446457
else:
447458
mesh = pv.Line(temp[0], temp[1])
448-
plotter.add_mesh(mesh, color=wavelength_to_rgb(ray.wl), line_width=2)
459+
plotter.plot(mesh, color=wavelength_to_rgb(ray.wl), line_width=2)
449460

461+
@graphics_required
450462
def preview(
451463
self,
452464
nb_ray: int = 100,
@@ -468,7 +480,8 @@ def preview(
468480
project : ansys.speos.core.project.Project
469481
Speos Project/Geometry to be added to pyvista visualisation.
470482
screenshot : str or Path or ``None``
471-
Path to save a screenshot of the plotter.
483+
Path to save a screenshot of the plotter. If defined Plotter will only create the
484+
screenshot
472485
473486
Returns
474487
-------
@@ -481,6 +494,8 @@ def preview(
481494
operating systems (namely Windows) will experience issues
482495
saving a screenshot if the exit button in the GUI is pressed.
483496
"""
497+
from ansys.tools.visualization_interface import Plotter
498+
484499
if ray_filter:
485500
if len(self._filtered_rays) > 0:
486501
temp_rays = self._filtered_rays
@@ -490,7 +505,7 @@ def preview(
490505
else:
491506
temp_rays = self._rays
492507
if not project:
493-
plotter = pv.Plotter()
508+
plotter = Plotter()
494509
if nb_ray > len(temp_rays):
495510
for ray in temp_rays:
496511
self.__add_ray_to_pv(plotter, ray, max_ray_length)

src/ansys/speos/core/project.py

+27-6
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@
2626
import os
2727
from pathlib import Path
2828
import re
29-
from typing import List, Mapping, Optional, Union
29+
from typing import TYPE_CHECKING, List, Mapping, Optional, Union
3030
import uuid
3131

3232
from google.protobuf.internal.containers import RepeatedScalarFieldContainer
3333
import numpy as np
34-
import pyvista as pv
3534

3635
import ansys.speos.core.body as body
3736
import ansys.speos.core.face as face
37+
from ansys.speos.core.generic.general_methods import graphics_required
3838
from ansys.speos.core.kernel.body import BodyLink
3939
from ansys.speos.core.kernel.face import FaceLink
4040
from ansys.speos.core.kernel.part import ProtoPart
@@ -59,6 +59,18 @@
5959
)
6060
from ansys.speos.core.speos import Speos
6161

62+
try:
63+
from ansys.speos.core.generic.general_methods import run_if_graphics_required
64+
65+
run_if_graphics_required(warning=True)
66+
except ImportError as err: # pragma: no cover
67+
raise err
68+
69+
if TYPE_CHECKING: # pragma: no cover
70+
import pyvista as pv
71+
72+
from ansys.tools.visualization_interface import Plotter
73+
6274

6375
class Project:
6476
"""A project describes all Speos features.
@@ -829,6 +841,7 @@ def __extract_part_mesh_info(
829841
pv.PolyData
830842
mesh data extracted.
831843
"""
844+
import pyvista as pv
832845

833846
def local2absolute(local_vertice: np.ndarray, coordinates) -> np.ndarray:
834847
"""Convert local coordinate to global coordinate.
@@ -888,7 +901,8 @@ def local2absolute(local_vertice: np.ndarray, coordinates) -> np.ndarray:
888901
part_mesh_info = part_mesh_info.append_polydata(face_mesh_data)
889902
return part_mesh_info
890903

891-
def _create_preview(self, viz_args=None) -> pv.Plotter:
904+
@graphics_required
905+
def _create_preview(self, viz_args=None) -> Plotter:
892906
"""Create preview pyvista plotter object.
893907
894908
Parameters
@@ -900,6 +914,10 @@ def _create_preview(self, viz_args=None) -> pv.Plotter:
900914
- {'style': 'surface', 'color':'white'},
901915
- {'opacity': 0.7, 'color':'white', 'show_edges': False},
902916
"""
917+
import pyvista as pv
918+
919+
from ansys.tools.visualization_interface import Plotter
920+
903921
if viz_args is None:
904922
viz_args = {}
905923
_preview_mesh = pv.PolyData()
@@ -921,10 +939,12 @@ def _create_preview(self, viz_args=None) -> pv.Plotter:
921939
poly_data = self.__extract_part_mesh_info(part_data=root_part_data)
922940
if poly_data is not None:
923941
_preview_mesh = _preview_mesh.append_polydata(poly_data)
924-
p = pv.Plotter()
925-
p.add_mesh(_preview_mesh, show_edges=True, **viz_args)
942+
p = Plotter()
943+
viz_args["show_edges"] = True
944+
p.plot(_preview_mesh, **viz_args)
926945
return p
927946

947+
@graphics_required
928948
def preview(
929949
self,
930950
viz_args=None,
@@ -942,7 +962,8 @@ def preview(
942962
- {'opacity': 0.7, 'color':'white', 'show_edges': False}.
943963
944964
screenshot : str or Path or ``None``
945-
Path to save a screenshot of the plotter.
965+
Path to save a screenshot of the plotter. If defined Plotter will only create the
966+
screenshot
946967
947968
"""
948969
if viz_args is None:

0 commit comments

Comments
 (0)