Skip to content

Commit 428d05d

Browse files
koubaaakaszynskigerma89
authored
decouple pyvista from mapdl and mapdl_grpc (#1129)
* decouple pyvista from mapdl and mapdl_grpc * style * fix null * PR review suggestions * fix formatting * typo * Update src/ansys/mapdl/core/mapdl_grpc.py Co-authored-by: Alex Kaszynski <[email protected]> * Update src/ansys/mapdl/core/mapdl_grpc.py Co-authored-by: Alex Kaszynski <[email protected]> * Removing more dependency * Adding more checks * Increasing coverage and removing some analyser errors * removing hint * Fixing unit test Co-authored-by: Alex Kaszynski <[email protected]> Co-authored-by: German <[email protected]>
1 parent 24f2f21 commit 428d05d

File tree

9 files changed

+166
-31
lines changed

9 files changed

+166
-31
lines changed

src/ansys/mapdl/core/mapdl.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
from warnings import warn
1414
import weakref
1515

16-
from ansys.mapdl.reader.rst import Result
1716
import numpy as np
1817

1918
from ansys.mapdl import core as pymapdl
@@ -36,11 +35,15 @@
3635
last_created,
3736
load_file,
3837
random_string,
38+
requires_package,
3939
run_as_prep7,
4040
supress_logging,
4141
wrap_point_SEL,
4242
)
43-
from ansys.mapdl.core.plotting import general_plotter
43+
44+
if _HAS_PYVISTA:
45+
from ansys.mapdl.core.plotting import general_plotter
46+
4447
from ansys.mapdl.core.post import PostProcessing
4548

4649
_PERMITTED_ERRORS = [
@@ -138,7 +141,7 @@ class _MapdlCore(Commands):
138141
def __init__(
139142
self,
140143
loglevel="DEBUG",
141-
use_vtk=True,
144+
use_vtk=None,
142145
log_apdl=None,
143146
log_file=False,
144147
local=True,
@@ -156,7 +159,10 @@ def __init__(
156159
self._response = None
157160

158161
if _HAS_PYVISTA:
159-
self._use_vtk = use_vtk
162+
if use_vtk is not None: # pragma: no cover
163+
self._use_vtk = use_vtk
164+
else:
165+
self._use_vtk = True
160166
else: # pragma: no cover
161167
if use_vtk:
162168
raise ModuleNotFoundError(
@@ -573,6 +579,7 @@ def info(self):
573579
return self._info
574580

575581
@property
582+
@requires_package("pyvista", softerror=True)
576583
def geometry(self):
577584
"""Geometry information.
578585
@@ -622,6 +629,7 @@ def _geometry(self): # pragma: no cover
622629
return Geometry(self)
623630

624631
@property
632+
@requires_package("pyvista", softerror=True)
625633
def mesh(self):
626634
"""Mesh information.
627635
@@ -668,6 +676,7 @@ def mesh(self):
668676
return self._mesh
669677

670678
@property
679+
@requires_package("ansys.mapdl.reader", softerror=True)
671680
@supress_logging
672681
def _mesh(self):
673682
"""Write entire archive to ASCII and read it in as an
@@ -1698,7 +1707,8 @@ def kplot(
16981707
return super().kplot(np1=np1, np2=np2, ninc=ninc, lab=lab, **kwargs)
16991708

17001709
@property
1701-
def result(self) -> Result:
1710+
@requires_package("ansys.mapdl.reader", softerror=True)
1711+
def result(self) -> "ansys.mapdl.reader.rst.Result":
17021712
"""Binary interface to the result file using :class:`ansys.mapdl.reader.rst.Result`.
17031713
17041714
Returns

src/ansys/mapdl/core/mapdl_geometry.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
import re
33

44
import numpy as np
5-
import pyvista as pv
5+
6+
from ansys.mapdl.core import _HAS_PYVISTA
7+
8+
if _HAS_PYVISTA:
9+
import pyvista as pv
610

711
from ansys.mapdl.core.misc import run_as_prep7, supress_logging
812

src/ansys/mapdl/core/mapdl_grpc.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
check_valid_ip,
5858
last_created,
5959
random_string,
60+
requires_package,
6061
run_as_prep7,
6162
supress_logging,
6263
)
@@ -506,10 +507,15 @@ def _connect(self, timeout=5, set_no_abort=True, enable_health_check=False):
506507
self._timer.start()
507508

508509
# initialize mesh, post processing, and file explorer interfaces
509-
from ansys.mapdl.core.mesh_grpc import MeshGrpc
510+
try:
511+
from ansys.mapdl.core.mesh_grpc import MeshGrpc
512+
513+
self._mesh_rep = MeshGrpc(self)
514+
except ModuleNotFoundError: # pragma: no cover
515+
self._mesh_rep = None
516+
510517
from ansys.mapdl.core.xpl import ansXpl
511518

512-
self._mesh_rep = MeshGrpc(self)
513519
self._post = PostProcessing(self)
514520
self._xpl = ansXpl(self)
515521

@@ -661,10 +667,14 @@ def _set_no_abort(self):
661667

662668
def _reset_cache(self):
663669
"""Reset cached items"""
664-
self._mesh_rep._reset_cache()
665-
self._geometry._reset_cache()
670+
if self._mesh_rep is not None:
671+
self._mesh_rep._reset_cache()
672+
673+
if self.geometry is not None:
674+
self._geometry._reset_cache()
666675

667676
@property
677+
@requires_package("pyvista")
668678
def _mesh(self):
669679
return self._mesh_rep
670680

@@ -2197,6 +2207,7 @@ def list_error_file(self):
21972207
return self._download_as_raw(error_file).decode("latin-1")
21982208

21992209
@property
2210+
@requires_package("ansys.mapdl.reader", softerror=True)
22002211
def result(self):
22012212
"""Binary interface to the result file using ``pyansys.Result``
22022213

src/ansys/mapdl/core/misc.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Module for miscellaneous functions and methods"""
22
from functools import wraps
3+
import importlib
34
import inspect
45
import os
56
import platform
@@ -974,6 +975,62 @@ def _get_load_step_options(self):
974975
return self._get_between(init_, end_string)
975976

976977

978+
def write_array(filename, array):
979+
"""
980+
Write an array to a file.
981+
982+
This function aim to replace
983+
``ansys.mapdl.reader._reader write_array``.
984+
985+
Parameters
986+
----------
987+
filename : str
988+
Name of the file.
989+
array : numpy.ndarray
990+
Array.
991+
"""
992+
np.savetxt(filename, array, fmt="%20.12f") # pragma: no cover
993+
994+
995+
def requires_package(package_name, softerror=False):
996+
"""
997+
Decorator check whether a package is installed or not.
998+
999+
If it is not, it will return None.
1000+
1001+
Parameters
1002+
----------
1003+
package_name : str
1004+
Name of the package.
1005+
"""
1006+
1007+
def decorator(function):
1008+
@wraps(function)
1009+
def wrapper(self, *args, **kwargs):
1010+
1011+
try:
1012+
importlib.import_module(package_name)
1013+
return function(self, *args, **kwargs)
1014+
1015+
except ModuleNotFoundError:
1016+
msg = (
1017+
f"To use the method '{function.__name__}', "
1018+
f"the package '{package_name}' is required.\n"
1019+
f"Please try to install '{package_name}' with:\n"
1020+
f"pip install {package_name.replace('.','-') if 'ansys' in package_name else package_name}"
1021+
)
1022+
1023+
if softerror:
1024+
warn(msg)
1025+
return None
1026+
else:
1027+
raise ModuleNotFoundError(msg)
1028+
1029+
return wrapper
1030+
1031+
return decorator
1032+
1033+
9771034
def _get_args_xsel(*args, **kwargs):
9781035
type_ = kwargs.pop("type_", args[0]).upper()
9791036
item = kwargs.pop("item", str(args[1]) if len(args) > 1 else "").upper()

src/ansys/mapdl/core/parameters.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@
22
import tempfile
33
import weakref
44

5-
from ansys.mapdl.reader._reader import write_array
5+
try:
6+
from ansys.mapdl.reader._reader import write_array
7+
8+
_HAS_READER = True
9+
except ModuleNotFoundError: # pragma: no cover
10+
from ansys.mapdl.core.misc import write_array
11+
12+
_HAS_READER = False
13+
614
import numpy as np
715

816
from ansys.mapdl.core.mapdl import _MapdlCore

src/ansys/mapdl/core/plotting.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -107,19 +107,19 @@ def get_VOLT():
107107

108108
HEAT = pv.Cube(center=(0, 0, 0), x_length=1.0, y_length=1.0, z_length=1.0)
109109

110-
BC_plot_settings = {
111-
"TEMP": {"color": "orange", "glyph": TEMP},
112-
"HEAT": {"color": "red", "glyph": HEAT},
113-
"UX": {"color": "red", "glyph": UX},
114-
"UY": {"color": "green", "glyph": UY},
115-
"UZ": {"color": "blue", "glyph": UZ},
116-
"VOLT": {"color": "yellow", "glyph": VOLT},
117-
"FX": {"color": "red", "glyph": FX},
118-
"FY": {"color": "green", "glyph": FY},
119-
"FZ": {"color": "blue", "glyph": FZ},
120-
"AMPS": {"color": "grey", "glyph": VOLT},
121-
"CHRGS": {"color": "grey", "glyph": VOLT},
122-
}
110+
BC_plot_settings = {
111+
"TEMP": {"color": "orange", "glyph": TEMP},
112+
"HEAT": {"color": "red", "glyph": HEAT},
113+
"UX": {"color": "red", "glyph": UX},
114+
"UY": {"color": "green", "glyph": UY},
115+
"UZ": {"color": "blue", "glyph": UZ},
116+
"VOLT": {"color": "yellow", "glyph": VOLT},
117+
"FX": {"color": "red", "glyph": FX},
118+
"FY": {"color": "green", "glyph": FY},
119+
"FZ": {"color": "blue", "glyph": FZ},
120+
"AMPS": {"color": "grey", "glyph": VOLT},
121+
"CHRGS": {"color": "grey", "glyph": VOLT},
122+
}
123123

124124
# Using * to force all the following arguments to be keyword only.
125125
def _general_plotter(

src/ansys/mapdl/core/post.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import weakref
33

44
import numpy as np
5-
from pyvista.plotting.renderer import CameraPosition
65

76
from ansys.mapdl.core.errors import MapdlRuntimeError
87
from ansys.mapdl.core.misc import supress_logging
@@ -467,7 +466,7 @@ def plot_nodal_values(self, item, comp, show_elem_numbering=False, **kwargs):
467466

468467
def plot_element_values(
469468
self, item, comp, option="AVG", show_elem_numbering=False, **kwargs
470-
) -> CameraPosition:
469+
):
471470
"""Plot element values.
472471
473472
Displays the solution results as discontinuous element contours.
@@ -1052,7 +1051,7 @@ def element_displacement(self, component="ALL", option="AVG") -> np.ndarray:
10521051

10531052
def plot_element_displacement(
10541053
self, component="NORM", option="AVG", show_elem_numbering=False, **kwargs
1055-
) -> CameraPosition:
1054+
):
10561055
"""Plot element displacement.
10571056
10581057
Parameters
@@ -1172,7 +1171,7 @@ def element_stress(self, component, option="AVG") -> np.ndarray:
11721171

11731172
def plot_element_stress(
11741173
self, component, option="AVG", show_elem_numbering=False, **kwargs
1175-
) -> CameraPosition:
1174+
):
11761175
"""Plot element component or principal stress.
11771176
11781177
One value per element. Either minimum, maximum, or average of
@@ -1277,7 +1276,7 @@ def element_temperature(self, option="AVG") -> np.ndarray:
12771276

12781277
def plot_element_temperature(
12791278
self, option="AVG", show_elem_numbering=False, **kwargs
1280-
) -> CameraPosition:
1279+
):
12811280
"""Plot element temperature.
12821281
12831282
One value per element. Either minimum, maximum, or average of

src/ansys/mapdl/core/theme.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,20 @@
11
"""Store parameters for a PyMAPDL-specific theme for pyvista"""
2-
from pyvista import themes
2+
from ansys.mapdl.core import _HAS_PYVISTA
33

4+
if _HAS_PYVISTA:
5+
from pyvista import themes
46

5-
class MapdlTheme(themes.DefaultTheme):
7+
base_class = themes.DefaultTheme
8+
9+
else: # pragma: no cover
10+
11+
class myEmptyClass:
12+
pass
13+
14+
base_class = myEmptyClass
15+
16+
17+
class MapdlTheme(base_class):
618
"""PyMAPDL-specific theme for pyvista.
719
820
Theme includes the following defaults:

tests/test_misc.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
last_created,
1616
load_file,
1717
no_return,
18+
requires_package,
1819
run_as_prep7,
1920
)
2021

@@ -278,3 +279,36 @@ def test_plain_report_no_options():
278279
assert "Core packages" in rep_str
279280
assert "Optional packages" not in rep_str
280281
assert "Additional packages" not in rep_str
282+
283+
284+
def test_requires_package_decorator():
285+
class myClass:
286+
@requires_package("numpy")
287+
def myfun(self):
288+
return True
289+
290+
@property
291+
@requires_package("numpy")
292+
def myfun2(self):
293+
return True
294+
295+
@property
296+
@requires_package("nuuumpy")
297+
def myotherfun(self):
298+
return False
299+
300+
@property
301+
@requires_package("nuuumpy", softerror=True)
302+
def myotherfun2(self):
303+
return False
304+
305+
myclass = myClass()
306+
307+
assert myclass.myfun()
308+
assert myclass.myfun2
309+
310+
with pytest.raises(ModuleNotFoundError):
311+
assert myclass.myotherfun
312+
313+
with pytest.warns(UserWarning):
314+
assert myclass.myotherfun2 is None

0 commit comments

Comments
 (0)