Skip to content

Commit a25ae7e

Browse files
authored
Improvements to open_gui (#1267)
* Implement the option to run inplace. Using original jobname. * Making sure the start_parameter is updated and stored. * Added MAPDLdidnotstart exception class. * Using new error class. * Updated docstring. * removing error classes. * small fix. * Making sure there is no edge cases in args * Fixing coverage and adding unit tests to error classes. * Updating docstrings * fixing error class inits * fixing init load
1 parent 18d2bc0 commit a25ae7e

File tree

5 files changed

+159
-38
lines changed

5 files changed

+159
-38
lines changed

src/ansys/mapdl/core/errors.py

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,20 @@ def __init__(self, msg=LOCKFILE_MSG):
7575
RuntimeError.__init__(self, msg)
7676

7777

78-
class LicenseServerConnectionError(RuntimeError):
79-
"""Error when the license server is not available."""
78+
class MapdlDidNotStart(RuntimeError):
79+
"""Error when the MAPDL process does not start"""
8080

8181
def __init__(self, msg=""):
8282
RuntimeError.__init__(self, msg)
8383

8484

85+
class LicenseServerConnectionError(MapdlDidNotStart):
86+
"""Error when the license server is not available."""
87+
88+
def __init__(self, msg=""):
89+
MapdlDidNotStart.__init__(self, msg)
90+
91+
8592
# handler for protect_grpc
8693
def handler(sig, frame): # pragma: no cover
8794
"""Pass signal to custom interrupt handler."""
@@ -144,3 +151,43 @@ def wrapper(*args, **kwargs):
144151
return out
145152

146153
return wrapper
154+
155+
156+
class MapdlException(MapdlRuntimeError):
157+
158+
"""General MAPDL exception."""
159+
160+
def __init__(self, msg=""):
161+
MapdlRuntimeError.__init__(self, msg)
162+
163+
164+
class MapdlError(MapdlException):
165+
166+
"""General MAPDL Error"""
167+
168+
def __init__(self, msg=""):
169+
MapdlException.__init__(self, msg)
170+
171+
172+
class MapdlWarning(MapdlException):
173+
174+
"""General MAPDL warning"""
175+
176+
def __init__(self, msg=""):
177+
MapdlException.__init__(self, msg)
178+
179+
180+
class MapdlNote(MapdlException):
181+
182+
"""General MAPDL note"""
183+
184+
def __init__(self, msg=""):
185+
MapdlException.__init__(self, msg)
186+
187+
188+
class MapdlInfo(MapdlException):
189+
190+
"""General MAPDL info message"""
191+
192+
def __init__(self, msg=""):
193+
MapdlException.__init__(self, msg)

src/ansys/mapdl/core/launcher.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
from ansys.mapdl import core as pymapdl
2424
from ansys.mapdl.core import LOG
25-
from ansys.mapdl.core.errors import LockFileException, VersionError
25+
from ansys.mapdl.core.errors import LockFileException, MapdlDidNotStart, VersionError
2626
from ansys.mapdl.core.licensing import ALLOWABLE_LICENSES, LicenseChecker
2727
from ansys.mapdl.core.mapdl import _MapdlCore
2828
from ansys.mapdl.core.mapdl_grpc import MAX_MESSAGE_LENGTH, MapdlGrpc
@@ -221,7 +221,7 @@ def launch_grpc(
221221
timeout=20,
222222
verbose=False,
223223
**kwargs,
224-
) -> tuple:
224+
) -> tuple: # pragma: no cover
225225
"""Start MAPDL locally in gRPC mode.
226226
227227
Parameters
@@ -496,9 +496,8 @@ def launch_grpc(
496496
]
497497
)
498498
command = " ".join(command_parm)
499-
499+
LOG.info(f"Running in {ip}:{port} the following command: '{command}'")
500500
if verbose:
501-
print(f"Running {command}")
502501
subprocess.Popen(command, shell=os.name != "nt", cwd=run_location)
503502
else:
504503
subprocess.Popen(
@@ -520,9 +519,15 @@ def launch_grpc(
520519
files = os.listdir(run_location)
521520
has_ans = any([filename for filename in files if ".err" in filename])
522521
if has_ans:
522+
LOG.info("MAPDL session successfully started (Error file found)")
523523
break
524524
time.sleep(sleep_time)
525525

526+
if not has_ans:
527+
raise MapdlDidNotStart(
528+
f"MAPDL failed to start (No err file generated in '{run_location}')"
529+
)
530+
526531
return port, run_location
527532

528533

src/ansys/mapdl/core/mapdl.py

Lines changed: 69 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import pathlib
88
import re
99
from shutil import copyfile, rmtree
10+
from subprocess import DEVNULL, call
1011
import tempfile
1112
import time
1213
import warnings
@@ -805,13 +806,21 @@ def _generate_iges(self):
805806
self.igesout(filename, att=1, mute=True)
806807
return filename
807808

808-
def open_gui(self, include_result=True): # pragma: no cover
809+
def open_gui(self, include_result=None, inplace=None): # pragma: no cover
809810
"""Saves existing database and opens up the APDL GUI.
810811
811812
Parameters
812813
----------
813814
include_result : bool, optional
814815
Allow the result file to be post processed in the GUI.
816+
It is ignored if 'inplace' is ``True``.
817+
By default, it is ``True``.
818+
819+
inplace : bool, optional
820+
Open the GUI on the current working directory, instead of create
821+
a new temporary directory and copy the results files over there.
822+
If ``True``, it ignores 'include_result' kwarg.
823+
By default, it is ``False``.
815824
816825
Examples
817826
--------
@@ -849,17 +858,38 @@ def open_gui(self, include_result=True): # pragma: no cover
849858
"``open_gui`` can only be called from a local " "MAPDL instance"
850859
)
851860

852-
# specify a path for the temporary database
853-
temp_dir = tempfile.gettempdir()
854-
save_path = os.path.join(temp_dir, f"ansys_{random_string(10)}")
855-
if os.path.isdir(save_path):
856-
rmtree(save_path)
857-
os.mkdir(save_path)
861+
if inplace and include_result:
862+
raise ValueError(
863+
"'inplace' and 'include_result' kwargs are not compatible."
864+
)
865+
866+
if inplace and include_result is None:
867+
include_result = False
868+
869+
elif not include_result:
870+
include_result = True
871+
872+
if not inplace:
873+
inplace = False
874+
875+
name = self.jobname
876+
877+
# specify a path for the temporary database if any.
878+
if inplace:
879+
run_dir = self._start_parm["run_location"]
858880

859-
name = "tmp"
860-
tmp_database = os.path.join(save_path, "%s.db" % name)
861-
if os.path.isfile(tmp_database):
862-
os.remove(tmp_database)
881+
else:
882+
temp_dir = tempfile.gettempdir()
883+
run_dir = os.path.join(temp_dir, f"ansys_{random_string(10)}")
884+
885+
# Sanity checks
886+
if os.path.isdir(run_dir):
887+
rmtree(run_dir)
888+
os.mkdir(run_dir)
889+
890+
database_file = os.path.join(run_dir, "%s.db" % name)
891+
if os.path.isfile(database_file) and not inplace:
892+
os.remove(database_file)
863893

864894
# cache result file, version, and routine before closing
865895
resultfile = self._result_file
@@ -868,40 +898,56 @@ def open_gui(self, include_result=True): # pragma: no cover
868898

869899
# finish, save and exit the server
870900
self.finish(mute=True)
871-
self.save(tmp_database, mute=True)
901+
self.save(database_file, mute=True)
872902
self.exit()
873903

874904
# copy result file to temp directory
875-
if include_result and self._result_file is not None:
876-
if os.path.isfile(resultfile):
877-
tmp_resultfile = os.path.join(save_path, "%s.rst" % name)
878-
copyfile(resultfile, tmp_resultfile)
905+
if not inplace:
906+
if include_result and self._result_file is not None:
907+
if os.path.isfile(resultfile):
908+
tmp_resultfile = os.path.join(run_dir, "%s.rst" % name)
909+
copyfile(resultfile, tmp_resultfile)
879910

880911
# write temporary input file
881-
start_file = os.path.join(save_path, "start%s.ans" % version)
912+
start_file = os.path.join(run_dir, "start%s.ans" % version)
882913
with open(start_file, "w") as f:
883914
f.write("RESUME\n")
884915

885916
# some versions of ANSYS just look for "start.ans" when starting
886-
other_start_file = os.path.join(save_path, "start.ans")
917+
other_start_file = os.path.join(run_dir, "start.ans")
887918
with open(other_start_file, "w") as f:
888919
f.write("RESUME\n")
889920

890921
# issue system command to run ansys in GUI mode
891922
cwd = os.getcwd()
892-
os.chdir(save_path)
923+
os.chdir(run_dir)
893924
exec_file = self._start_parm.get("exec_file", get_ansys_path(allow_input=False))
894925
nproc = self._start_parm.get("nproc", 2)
895926
add_sw = self._start_parm.get("additional_switches", "")
896-
os.system(
897-
f'cd "{save_path}" && "{exec_file}" -g -j {name} -np {nproc} {add_sw}'
927+
928+
if inplace:
929+
warn(
930+
"MAPDL GUI is opened using 'inplace' kwarg."
931+
f"Hence the changes you do will overwrite the files in {run_dir}."
932+
)
933+
934+
call(
935+
f'cd "{run_dir}" && "{exec_file}" -g -j {name} -np {nproc} {add_sw}',
936+
shell=True,
937+
stdout=DEVNULL,
898938
)
939+
940+
# Going back
899941
os.chdir(cwd)
900-
# Consider removing this temporary directory
942+
943+
# Clearing
944+
os.remove(start_file)
945+
os.remove(other_start_file)
901946

902947
# reattach to a new session and reload database
903948
self._launch(self._start_parm)
904-
self.resume(tmp_database, mute=True)
949+
self.resume(database_file, mute=True)
950+
self.save()
905951

906952
def _cache_routine(self):
907953
"""Cache the current routine."""

src/ansys/mapdl/core/mapdl_grpc.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ def __init__(
280280
print_com=False,
281281
channel=None,
282282
remote_instance=None,
283-
**kwargs,
283+
**start_parm,
284284
):
285285
"""Initialize connection to the mapdl server"""
286286
self.__distributed = None
@@ -302,7 +302,7 @@ def __init__(
302302
log_apdl=log_apdl,
303303
log_file=log_file,
304304
print_com=print_com,
305-
**kwargs,
305+
**start_parm,
306306
)
307307

308308
# gRPC request specific locks as these gRPC request are not thread safe
@@ -314,18 +314,23 @@ def __init__(
314314
self._stub = None
315315
self._cleanup = cleanup_on_exit
316316
self._remove_tmp = remove_temp_files
317-
self._jobname = kwargs.pop("jobname", "file")
318-
self._path = kwargs.pop("run_location", None)
317+
self._jobname = start_parm.get("jobname", "file")
318+
self._path = start_parm.get("run_location", None)
319319
self._busy = False # used to check if running a command on the server
320320
self._local = ip in ["127.0.0.1", "127.0.1.1", "localhost"]
321-
if "local" in kwargs: # pragma: no cover # allow this to be overridden
322-
self._local = kwargs["local"]
321+
if "local" in start_parm: # pragma: no cover # allow this to be overridden
322+
self._local = start_parm["local"]
323323
self._health_response_queue = None
324324
self._exiting = False
325325
self._exited = None
326326
self._mute = False
327327
self._db = None
328328

329+
# saving for later use (for example open_gui)
330+
start_parm["ip"] = ip
331+
start_parm["port"] = port
332+
self._start_parm = start_parm
333+
329334
if port is None:
330335
from ansys.mapdl.core.launcher import MAPDL_DEFAULT_PORT
331336

@@ -345,11 +350,11 @@ def __init__(
345350

346351
# double check we have access to the local path if not
347352
# explicitly specified
348-
if "local" not in kwargs:
353+
if "local" not in start_parm:
349354
self._verify_local()
350355

351356
# only cache process IDs if launched locally
352-
if self._local and "exec_file" in kwargs:
357+
if self._local and "exec_file" in start_parm:
353358
self._cache_pids()
354359

355360
def _create_channel(self, ip, port):

tests/test_errors.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
import pytest
22

3-
from ansys.mapdl.core.errors import MapdlRuntimeError
3+
from ansys.mapdl.core.errors import (
4+
MapdlDidNotStart,
5+
MapdlError,
6+
MapdlException,
7+
MapdlInfo,
8+
MapdlNote,
9+
MapdlRuntimeError,
10+
MapdlWarning,
11+
)
412

513
error_shape_error_limits = """
614
@@ -79,3 +87,13 @@ def test_raise_output_errors(mapdl, response, expected_error):
7987
mapdl._raise_output_errors(response)
8088
else:
8189
mapdl._raise_output_errors(response)
90+
91+
92+
@pytest.mark.parametrize(
93+
"error_class",
94+
[MapdlDidNotStart, MapdlException, MapdlError, MapdlWarning, MapdlNote, MapdlInfo],
95+
)
96+
def test_exception_classes(error_class):
97+
message = "Exception message"
98+
with pytest.raises(error_class):
99+
raise error_class(message)

0 commit comments

Comments
 (0)