diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml
index f09e15d225..d8f10e35c1 100644
--- a/.github/ISSUE_TEMPLATE/bug.yml
+++ b/.github/ISSUE_TEMPLATE/bug.yml
@@ -14,11 +14,13 @@ body:
id: new-bug
attributes:
label: Before submitting the issue
- description: Please, make sure the following conditions are met
+ description: Ensure that the following conditions are met:
options:
- - label: I have searched among the existing issues
+ - label: I have visited the [Troubleshooting section](https://mapdl.docs.pyansys.com/troubleshoot/index.html).
required: true
- - label: I am using a Python virtual environment
+ - label: I have searched among the existing issues.
+ required: true
+ - label: I am using a Python virtual environment.
required: true
- label: I have a fully updated virtual environment (i.e. ``pip install --upgrade --upgrade-strategy eager ansys-mapdl-core``)
required: true
@@ -134,4 +136,28 @@ body:
validations:
- required: true
\ No newline at end of file
+ required: true
+
+ - type: textarea
+ id: logger_log
+ attributes:
+ label: Logger output file
+ description: |
+ Attach the logger output file. For more information on how to set the logger and
+ attach its output file, see the [Troubleshooting section]
+ (https://mapdl.docs.pyansys.com/troubleshoot/index.html).
+
+ value: |
+
+ Show the logger output file.
+
+
+ ```text
+
+ # PASTE HERE THE CONTENT OF THE LOGGER OUTPUT FILE.
+
+ ```
+
+
+ validations:
+ required: false
\ No newline at end of file
diff --git a/doc/source/troubleshoot/index.rst b/doc/source/troubleshoot/index.rst
index ca677272d0..7453f931da 100644
--- a/doc/source/troubleshoot/index.rst
+++ b/doc/source/troubleshoot/index.rst
@@ -15,6 +15,33 @@ some of the most common problems and frequently asked questions are posted here.
faq
+Debug in PyMAPDL
+----------------
+
+If you are having trouble with PyMAPDL, you can examine the content
+of the output file to help to identify any issue.
+
+You can set the logger output file to be ``mylog.log`` by
+running the following commands in a Python terminal or at the beginning of your
+script:
+
+.. code:: python
+
+ from ansys.mapdl.core import LOG
+ LOG.setLevel("DEBUG")
+ LOG.log_to_file("mylog.log")
+
+ from ansys.mapdl.core import launch_mapdl
+
+ mapdl = launch_mapdl(loglevel="DEBUG")
+
+You can attach this file to a bug report in the PyMAPDL GitHub repository for further investigation.
+If you are not able to identify the issue, you can open a discussion on the
+`PyMAPDL Discussions page `_.
+If you believe you have found a bug, open an issue on the
+`PyMAPDL Issues page `_.
+
+
More help needed?
-----------------
diff --git a/src/ansys/mapdl/core/__init__.py b/src/ansys/mapdl/core/__init__.py
index 30c20e2c01..e420c7be8c 100644
--- a/src/ansys/mapdl/core/__init__.py
+++ b/src/ansys/mapdl/core/__init__.py
@@ -11,6 +11,9 @@
_LOCAL_PORTS = []
+LINUX_DEFAULT_DIRS = [["/", "usr", "ansys_inc"], ["/", "ansys_inc"]]
+LINUX_DEFAULT_DIRS = [os.path.join(*each) for each in LINUX_DEFAULT_DIRS]
+
# Per contract with Sphinx-Gallery, this method must be available at top level
try:
from pyvista.utilities.sphinx_gallery import _get_sg_image_scraper
diff --git a/src/ansys/mapdl/core/errors.py b/src/ansys/mapdl/core/errors.py
index 0e383f330a..1d71c52eaa 100644
--- a/src/ansys/mapdl/core/errors.py
+++ b/src/ansys/mapdl/core/errors.py
@@ -89,8 +89,15 @@ def __init__(self, msg=""):
RuntimeError.__init__(self, msg)
+class MapdlConnectionError(RuntimeError):
+ """Provides the error when connecting to the MAPDL instance fails."""
+
+ def __init__(self, msg=""):
+ RuntimeError.__init__(self, msg)
+
+
class LicenseServerConnectionError(MapdlDidNotStart):
- """Error when the license server is not available."""
+ """Provides the error when the license server is not available."""
def __init__(self, msg=""):
MapdlDidNotStart.__init__(self, msg)
diff --git a/src/ansys/mapdl/core/launcher.py b/src/ansys/mapdl/core/launcher.py
index fe311161db..c5cc91e7c6 100644
--- a/src/ansys/mapdl/core/launcher.py
+++ b/src/ansys/mapdl/core/launcher.py
@@ -21,7 +21,7 @@
import appdirs
from ansys.mapdl import core as pymapdl
-from ansys.mapdl.core import LOG
+from ansys.mapdl.core import LINUX_DEFAULT_DIRS, LOG
from ansys.mapdl.core._version import SUPPORTED_ANSYS_VERSIONS
from ansys.mapdl.core.errors import LockFileException, MapdlDidNotStart, VersionError
from ansys.mapdl.core.licensing import ALLOWABLE_LICENSES, LicenseChecker
@@ -42,6 +42,7 @@
if not os.path.isdir(SETTINGS_DIR):
try:
os.makedirs(SETTINGS_DIR)
+ LOG.debug(f"Created settings directory: {SETTINGS_DIR}")
except:
warnings.warn(
"Unable to create settings directory.\n"
@@ -51,6 +52,7 @@
CONFIG_FILE = os.path.join(SETTINGS_DIR, "config.txt")
ALLOWABLE_MODES = ["corba", "console", "grpc"]
+
LOCALHOST = "127.0.0.1"
MAPDL_DEFAULT_PORT = 50052
@@ -401,14 +403,17 @@ def launch_grpc(
>>> mapdl = launch_mapdl(exec_file, additional_switches='-smp')
"""
+ LOG.debug("Starting 'launch_mapdl'.")
# disable all MAPDL pop-up errors:
os.environ["ANS_CMD_NODIAG"] = "TRUE"
# use temporary directory if run_location is unspecified
if run_location is None:
run_location = create_temp_dir()
+ LOG.debug(f"Using temporary directory for MAPDL run location: {run_location}")
elif not os.path.isdir(run_location):
os.mkdir(run_location)
+ LOG.debug(f"Creating directory for MAPDL run location: {run_location}")
if not os.access(run_location, os.W_OK):
raise IOError('Unable to write to ``run_location`` "%s"' % run_location)
@@ -424,21 +429,26 @@ def launch_grpc(
if port is None:
if not pymapdl._LOCAL_PORTS:
port = MAPDL_DEFAULT_PORT
+ LOG.debug(f"Using default port: {port}")
else:
port = max(pymapdl._LOCAL_PORTS) + 1
+ LOG.debug(f"Using next available port: {port}")
while port_in_use(port) or port in pymapdl._LOCAL_PORTS:
port += 1
+ LOG.debug(f"Port in use. Incrementing port number. port={port}")
pymapdl._LOCAL_PORTS.append(port)
# setting ip for the grpc server
if ip != LOCALHOST: # Default local ip is 127.0.0.1
create_ip_file(ip, run_location)
+ LOG.debug(f"Writing ip ({ip}) in 'mylocal.ip' file.")
cpu_sw = "-np %d" % nproc
if ram:
ram_sw = "-m %d" % int(1024 * ram)
+ LOG.debug(f"Setting RAM: {ram_sw}")
else:
ram_sw = ""
@@ -455,6 +465,7 @@ def launch_grpc(
if os.path.isfile(filename):
try:
os.remove(filename)
+ LOG.debug(f"Removing temporary error file: {filename}")
except:
raise IOError(
f"Unable to remove {filename}. There might be "
@@ -467,6 +478,7 @@ def launch_grpc(
tmp_inp = ".__tmp__.inp"
with open(os.path.join(run_location, tmp_inp), "w") as f:
f.write("FINISH\r\n")
+ LOG.debug(f"Writing temporary input file: {tmp_inp} with 'FINISH' command.")
# must start in batch mode on windows to hide APDL window
command_parm = [
@@ -500,6 +512,8 @@ def launch_grpc(
)
command = " ".join(command_parm)
+ LOG.debug(f"Starting MAPDL with command: {command}")
+
env_vars = update_env_vars(add_env_vars, replace_env_vars)
LOG.info(f"Running in {ip}:{port} the following command: '{command}'")
@@ -517,6 +531,7 @@ def launch_grpc(
stderr=subprocess.DEVNULL,
env=env_vars,
)
+ LOG.debug("MAPDL started in background.")
# watch for the creation of temporary files at the run_directory.
# This lets us know that the MAPDL process has at least started
@@ -528,7 +543,7 @@ def launch_grpc(
files = os.listdir(run_location)
has_ans = any([filename for filename in files if ".err" in filename])
if has_ans:
- LOG.info("MAPDL session successfully started (Error file found)")
+ LOG.debug("MAPDL session successfully started (Error file found)")
break
time.sleep(sleep_time)
@@ -614,7 +629,14 @@ def get_start_instance(start_instance_default=True):
f'Invalid value "{val}" for PYMAPDL_START_INSTANCE\n'
'PYMAPDL_START_INSTANCE should be either "TRUE" or "FALSE"'
)
+ LOG.debug(
+ f"PYMAPDL_START_INSTANCE is set to {os.environ['PYMAPDL_START_INSTANCE']}"
+ )
return os.environ["PYMAPDL_START_INSTANCE"].lower() == "true"
+
+ LOG.debug(
+ f"PYMAPDL_START_INSTANCE is unset, using default value {start_instance_default}"
+ )
return start_instance_default
@@ -670,6 +692,9 @@ def _get_available_base_ansys():
}
if installed_versions:
+ LOG.debug(
+ f"Found the following installed Ansys versions: {installed_versions}"
+ )
return installed_versions
else: # pragma: no cover
LOG.debug(
@@ -682,7 +707,7 @@ def _get_available_base_ansys():
)
return {}
elif os.name == "posix":
- for path in ["/usr/ansys_inc", "/ansys_inc"]:
+ for path in LINUX_DEFAULT_DIRS:
if os.path.isdir(path):
base_path = path
else: # pragma: no cover
@@ -962,6 +987,7 @@ def warn_uncommon_executable_path(exe_loc): # pragma: no cover
def check_lock_file(path, jobname, override):
+ LOG.debug("Checking for lock file")
# Check for lock file
lockfile = os.path.join(path, jobname + ".lock")
if os.path.isfile(lockfile):
@@ -975,6 +1001,7 @@ def check_lock_file(path, jobname, override):
else:
try:
os.remove(lockfile)
+ LOG.debug("Removed lock file")
except PermissionError:
raise LockFileException(
"Unable to remove lock file. "
@@ -1410,6 +1437,9 @@ def launch_mapdl(
if ip is None:
ip = os.environ.get("PYMAPDL_IP", LOCALHOST)
else: # pragma: no cover
+ LOG.debug(
+ "Because ``PYMAPDL_IP is not None, an attempt is made to connect to a remote session. ('START_INSTANCE' is set to False.`)"
+ )
start_instance = False
ip = socket.gethostbyname(ip) # Converting ip or hostname to ip
@@ -1418,6 +1448,7 @@ def launch_mapdl(
if port is None:
port = int(os.environ.get("PYMAPDL_PORT", MAPDL_DEFAULT_PORT))
check_valid_port(port)
+ LOG.debug(f"Using default port {port}")
# Start MAPDL with PyPIM if the environment is configured for it
# and the user did not pass a directive on how to launch it.
@@ -1427,14 +1458,25 @@ def launch_mapdl(
# connect to an existing instance if enabled
if start_instance is None:
+ if "PYMAPDL_START_INSTANCE" in os.environ:
+ LOG.debug("Using PYMAPDL_START_INSTANCE environment variable.")
+
+ if os.environ.get("PYMAPDL_START_INSTANCE") and os.environ.get(
+ "PYMAPDL_START_INSTANCE"
+ ).lower() not in ["true", "false"]:
+ raise ValueError("PYMAPDL_START_INSTANCE must be either True or False.")
+
start_instance = check_valid_start_instance(
os.environ.get("PYMAPDL_START_INSTANCE", True)
)
+ LOG.debug("Using 'start_instance' equal to %s", start_instance)
+
# special handling when building the gallery outside of CI. This
# creates an instance of mapdl the first time if PYMAPDL start instance
# is False.
if pymapdl.BUILDING_GALLERY: # pragma: no cover
+ LOG.debug("Building gallery.")
# launch an instance of pymapdl if it does not already exist and
# we're allowed to start instances
if start_instance and GALLERY_INSTANCE[0] is None:
@@ -1474,6 +1516,7 @@ def launch_mapdl(
return mapdl
if not start_instance:
+ LOG.debug("Connecting to an existing instance of MAPDL at %s:%s", ip, port)
if clear_on_connect is None: # pragma: no cover
clear_on_connect = False
@@ -1490,6 +1533,7 @@ def launch_mapdl(
# verify executable
if exec_file is None:
+ LOG.debug("Using default executable.")
# Load cached path
exec_file = get_ansys_path()
if exec_file is None:
@@ -1507,11 +1551,13 @@ def launch_mapdl(
# verify run location
if run_location is None:
+ LOG.debug("Using default run location.")
temp_dir = tempfile.gettempdir()
run_location = os.path.join(temp_dir, "ansys_%s" % random_string(10))
if not os.path.isdir(run_location):
try:
os.mkdir(run_location)
+ LOG.debug("Created run location at %s", run_location)
except:
raise RuntimeError(
"Unable to create the temporary working "
@@ -1525,9 +1571,13 @@ def launch_mapdl(
LOG.info("`run_location` set. Disabling the removal of temporary files.")
remove_temp_dir_on_exit = False
+ LOG.debug("Using run location at %s", run_location)
+
# verify no lock file and the mode is valid
check_lock_file(run_location, jobname, override)
+
mode = check_mode(mode, _version_from_path(exec_file))
+ LOG.debug("Using mode %s", mode)
# Setting SMP by default if student version is used.
additional_switches = _force_smp_student_version(additional_switches, exec_file)
@@ -1538,6 +1588,7 @@ def launch_mapdl(
)
additional_switches = _check_license_argument(license_type, additional_switches)
+ LOG.debug(f"Using additional switches {additional_switches}.")
start_parm = {
"exec_file": exec_file,
@@ -1555,14 +1606,18 @@ def launch_mapdl(
start_parm["override"] = override
start_parm["timeout"] = start_timeout
+ LOG.debug(f"Using start parameters {start_parm}")
+
# Check the license server
if license_server_check:
# configure timeout to be 90% of the wait time of the startup
# time for Ansys.
+ LOG.debug("Checking license server.")
lic_check = LicenseChecker(timeout=start_timeout * 0.9)
lic_check.start()
try:
+ LOG.debug("Starting MAPDL")
if mode == "console":
from ansys.mapdl.core.mapdl_console import MapdlConsole
@@ -1610,12 +1665,14 @@ def launch_mapdl(
# Failed to launch for some reason. Check if failure was due
# to the license check
if license_server_check:
+ LOG.debug("Checking license server.")
lic_check.check()
raise exception
# Stopping license checker
if license_server_check:
+ LOG.debug("Stopping license server check.")
lic_check.is_connected = True
return mapdl
@@ -1716,6 +1773,7 @@ def update_env_vars(add_env_vars, replace_env_vars):
)
add_env_vars.update(os.environ)
+ LOG.debug(f"Updating environment variables with: {add_env_vars}")
return add_env_vars
elif replace_env_vars:
@@ -1723,7 +1781,7 @@ def update_env_vars(add_env_vars, replace_env_vars):
raise TypeError(
"The variable 'replace_env_vars' should be a dict with env vars."
)
-
+ LOG.debug(f"Replacing environment variables with: {replace_env_vars}")
return replace_env_vars
diff --git a/src/ansys/mapdl/core/mapdl_grpc.py b/src/ansys/mapdl/core/mapdl_grpc.py
index afa8602d4d..d5bf581f01 100755
--- a/src/ansys/mapdl/core/mapdl_grpc.py
+++ b/src/ansys/mapdl/core/mapdl_grpc.py
@@ -53,7 +53,12 @@
DEFAULT_FILE_CHUNK_SIZE,
parse_chunks,
)
-from ansys.mapdl.core.errors import MapdlExitedError, MapdlRuntimeError, protect_grpc
+from ansys.mapdl.core.errors import (
+ MapdlConnectionError,
+ MapdlExitedError,
+ MapdlRuntimeError,
+ protect_grpc,
+)
from ansys.mapdl.core.mapdl import _MapdlCore
from ansys.mapdl.core.mapdl_types import MapdlInt
from ansys.mapdl.core.misc import (
@@ -361,8 +366,10 @@ def __init__(
self._pids = []
if channel is None:
+ self._log.debug("Creating channel to %s:%s", ip, port)
self._channel = self._create_channel(ip, port)
else:
+ self._log.debug("Using provided channel")
self._channel = channel
# connect and validate to the channel
@@ -428,8 +435,8 @@ def _multi_connect(self, n_attempts=5, timeout=15, set_no_abort=True):
)
if not connected:
- raise IOError(
- f"Unable to connect to MAPDL gRPC instance at {self._channel_str}"
+ raise MapdlConnectionError(
+ f"Unable to connect to MAPDL gRPC instance at {self._channel_str}."
)
else:
self._exited = False
diff --git a/src/ansys/mapdl/core/misc.py b/src/ansys/mapdl/core/misc.py
index c887dc255f..bd599486d3 100644
--- a/src/ansys/mapdl/core/misc.py
+++ b/src/ansys/mapdl/core/misc.py
@@ -18,7 +18,7 @@
import numpy as np
from ansys.mapdl import core as pymapdl
-from ansys.mapdl.core import _HAS_PYVISTA, LOG
+from ansys.mapdl.core import _HAS_PYVISTA, LINUX_DEFAULT_DIRS, LOG
try:
import ansys.tools.report as pyansys_report
@@ -82,21 +82,54 @@ def check_valid_routine(routine):
return True
+def get_linux_default_ansys_bin(rver):
+ """Find the MAPDL executable file using standard Linux installation paths,
+
+ Raises:
+ FileNotFoundError: When no binary is found.
+
+ Returns:
+ str: Path to MAPDL executable.
+ """
+ for each_path in LINUX_DEFAULT_DIRS:
+ for each_file in [f"ansys{rver}", "mapdl"]:
+
+ ans_root = os.getenv(f"AWP_ROOT{rver}", each_path)
+ mapdlbin = os.path.join(ans_root, f"v{rver}", "ansys", "bin", each_file)
+
+ # rare case where the versioned binary doesn't exist
+ if os.path.isfile(mapdlbin):
+ LOG.debug(f"Found ANSYS binary at {mapdlbin}")
+ return mapdlbin
+ else:
+ LOG.debug(f"NOT found Ansys binary at {mapdlbin}")
+
+ # We could not find a binary, returning a default one
+ return os.path.join(
+ LINUX_DEFAULT_DIRS[0], f"v{rver}", "ansys", "bin", f"ansys{rver}"
+ )
+
+
+def get_windows_default_ansys_bin(rver):
+ """Find the MAPDL executable using standard Windows installation paths"""
+ program_files = os.getenv("PROGRAMFILES", os.path.join("c:\\", "Program Files"))
+ ans_root = os.getenv(
+ f"AWP_ROOT{rver}", os.path.join(program_files, "ANSYS Inc", f"v{rver}")
+ )
+ return os.path.join(ans_root, "ansys", "bin", "winx64", f"ANSYS{rver}.exe")
+
+
def get_ansys_bin(rver):
"""Identify the ansys executable based on the release version (e.g. "201")"""
- if os.name == "nt": # pragma: no cover
- program_files = os.getenv("PROGRAMFILES", os.path.join("c:\\", "Program Files"))
- ans_root = os.getenv(
- f"AWP_ROOT{rver}", os.path.join(program_files, "ANSYS Inc", f"v{rver}")
+ if os.getenv(f"AWP_ROOT{rver}") is not None:
+ LOG.debug(
+ f"Found 'AWP_ROOT{rver}' environment variable and it has value {os.getenv(f'AWP_ROOT{rver}')}"
)
- mapdlbin = os.path.join(ans_root, "ansys", "bin", "winx64", f"ANSYS{rver}.exe")
- else:
- ans_root = os.getenv(f"AWP_ROOT{rver}", os.path.join("/", "usr", "ansys_inc"))
- mapdlbin = os.path.join(ans_root, f"v{rver}", "ansys", "bin", f"ansys{rver}")
- # rare case where the versioned binary doesn't exist
- if not os.path.isfile(mapdlbin):
- mapdlbin = os.path.join(ans_root, f"v{rver}", "ansys", "bin", "mapdl")
+ if os.name == "nt": # pragma: no cover
+ mapdlbin = get_windows_default_ansys_bin(rver)
+ else:
+ mapdlbin = get_linux_default_ansys_bin(rver)
return mapdlbin