Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Supporting launching MAPDL from WSL #2324

Merged
merged 19 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/source/getting_started/macos.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Launch MAPDL on MacOS
=====================

If you do not have an MAPDL Docker image, you can create one on a Linux
machine as indicated in :ref:`ref_make_container`.
machine as indicated in :ref:`ref_make_container`.


If you already have an MAPDL Docker image, you can launch MAPDL as
Expand Down
104 changes: 82 additions & 22 deletions doc/source/getting_started/wsl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,17 @@ Windows Server 2019. For more information, see:
This page walk you through the installation of WSL on Windows and then
show how to use it together with MAPDL, PyMAPDL, and `Docker <https://www.docker.com/>`_.

.. caution::
This approach hasn't been fully tested with a VPN connection. If you
experience any problems connecting WSL to the internet, try to
disconnect from the VPN.

.. note::
Because WSL is under constant development, keeping this guide updated is difficult. If you
find any issues or have questions related to WSL, feel free to `open an issue in the GitHub repository <pymapdl_issues>`_.

Run PyMAPDL on WSL
##################
There are two versions of WSL: WSL1 and WSL2. Because WSL2 provides many improvements
over WSL1, you should upgrade to and use WSL2.

Install WSL
============
###########

There are two versions of WSL: WSL1 and WSL2. Because WSL2 provides many improvements
over WSL1, you should upgrade to and use WSL2.

Install WSL by following Microsoft's directions at
`Microsoft: Install WSL <install_wsl_microsoft_>`_.
Expand All @@ -41,14 +39,23 @@ You can install this distribution using an unofficial WSL distribution from
`CentOS-WSL <gh_centos_wsl_1_>`_ package or the
`CentOS WSL <gh_centos_wsl_2_>`_ package.

Optionally, you can try Ubuntu, but it has not been tested yet in the context of WSL.

.. vale off

Using the Ubuntu WSL distribution
=================================

.. vale on

Install Ansys products in WSL CentOS 7
======================================
Ubuntu is a supported operative system for Ansys products. However it has not been
tested yet in the context of WSL. You should proceed with caution.


Install Ansys products in WSL
#############################

Prerequisites
~~~~~~~~~~~~~
=============
If you are using CentOS 7, before installing MAPDL, you must install some
required libraries:

Expand All @@ -63,7 +70,7 @@ If you are using Ubuntu, follow the instructions in `Run MAPDL: Ubuntu <pymapdl_
.. _installing_ansys_in_wsl:

Install Ansys products
~~~~~~~~~~~~~~~~~~~~~~
======================

To install Ansys products in WSL Linux, perform these steps:

Expand Down Expand Up @@ -119,10 +126,10 @@ directory (``/*/ansys_inc``).


Post-installation setup
=======================
#######################

Open ports for license server communication
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
===========================================

**Theory:** You should open the ports ``1055`` and ``2325`` for license server
communication in the **Windows Control Panel**. For the steps to set advanced
Expand All @@ -146,7 +153,7 @@ side effects and security risk so use it with caution. For more information, see


Set up an environmental variable in WSL that points to Windows host license server
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
==================================================================================

The IP address for the Windows host is given in the WSL ``/etc/hosts`` file before the name
``host.docker.internal``.
Expand Down Expand Up @@ -184,21 +191,25 @@ Here is an example of the WSL ``/etc/hosts`` file:

.. vale on

You can add the next lines to your WSL ``~/.bashrc`` file to create an
You can add the next lines to your WSL ``=/.bashrc`` file to create an
environment variable with this IP address:

.. vale off

.. _ref_bash_win_ip:

.. vale on

.. code:: console

winhostIP=$(grep -m 1 host.docker.internal /etc/hosts | awk '{print $1}')
export ANSYSLMD_LICENSE_FILE=1055@$winhostIP


Launch MAPDL in WSL
===================
###################

To launch MAPDL in WSL, you must launch MAPDL process.
To launch MAPDL in WSL, you must launch the MAPDL process.
An example follows.

.. code:: console
Expand All @@ -213,8 +224,48 @@ If you want to change the working directory, you can use the ``-dir`` flag.
/ansys_inc/v222/ansys/bin/ansys222 -grpc -dir /tmp/ansys_jobs/myjob


Launch MAPDL in the Windows host OS
###################################

You can launch an instance of MAPDL using the MAPDL installation from the
Windows host OS.
To do that, run this code:

.. code:: python

from ansys.mapdl.core import launch_mapdl

mapdl = launch_mapdl(
exec_file="/mnt/c/Program Files/ANSYS Inc/v231/ANSYS/bin/winx64/ANSYS231.exe",
)

As mentioned in `Open ports for license server communication`_, the Windows host OS
and WSL are connected with a virtual network where they both have different IP addresses.
PyMAPDL does its best to detect the IP address of the Windows host OS. For that, it parses
the output given by the command ``ip route`` in WSL. However, if you find that this IP
address is not correct, you can specify the IP address to connect to like this:

.. code:: python

from ansys.mapdl.core import launch_mapdl

mapdl = launch_mapdl(
exec_file="/mnt/c/Program Files/ANSYS Inc/v231/ANSYS/bin/winx64/ANSYS231.exe",
ip="172.23.112.1",
)

You might need to turn off the Microsoft Firewall completely or at least
for the WSL network connection.
To do so, follow
:ref:`Disable firewall on WSL ethernet <disable_firewall_on_wsl_ethernet_section>`.


For more information, see the issue `Launching MAPDL from WSL <wsl_launching_mapdl_>`_
or open a new issue in the `GitHub repository issues <pymapdl_issues_>`_.


Connect to an MAPDL instance running in WSL
===========================================
###########################################

To connect to the WSL instance that is running the MAPDL instance,
you need to specify the IP address of the WSL instance:
Expand All @@ -225,6 +276,7 @@ you need to specify the IP address of the WSL instance:
>>> mapdl = Mapdl(ip="127.0.0.1", port=50053)



Additional information
######################

Expand Down Expand Up @@ -430,12 +482,20 @@ as shown in the following command. For more information, see the ``.ci`` folder.
yum install xorg-x11-server-Xvfb


.. note::
If you want to replicate the CI/CD behavior or develop from inside a Docker container,
you should use Ubuntu as your base operative system. You can find instructions
to create your own MAPDL Ubuntu container in :ref:`ref_make_container` and how to use
it to develop on containers in :ref:`ref_devcontainer`.

Notes
=====

- PyMAPDL only works for shared-memory parallel (SMP) when running on WSL. This
is why the flag ``-smp`` should be included.

- Because there are some incompatibilities between VPN and INTEL MPI, use the
flag ``-mpi msmpi`` when calling MAPDL.
flag ``-mpi msmpi`` when calling MAPDL. This WSL guidance has not been written for or tested
on VPN. If you are experiencing issues connecting to the Windows host machine,
your license server, or an MAPDL instance, disconnect the VPN and try again.

1 change: 1 addition & 0 deletions doc/source/links.rst
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@
.. _pymapdl_docker_compose_license_server: https://github.com/ansys/pymapdl/blob/main/docker/docker-compose.license_server.yml
.. _pymapdl_discussion_differences_mapdl_pymapdl: https://github.com/ansys/pymapdl-reader/issues/185
.. _cartpole_example_notebook: https://cartpole.mapdl.docs.pyansys.com/ml-rl-notebook.html
.. _wsl_launching_mapdl: https://github.com/ansys/pymapdl/issues/2315

.. #Python
.. _using_venv: https://docs.python.org/3/library/venv.html
Expand Down
70 changes: 64 additions & 6 deletions src/ansys/mapdl/core/launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,18 @@
CONFIG_FILE = os.path.join(SETTINGS_DIR, "config.txt")
ALLOWABLE_MODES = ["corba", "console", "grpc"]

ON_WSL = os.name == "posix" and (
bool(os.environ.get("WSL_DISTRO_NAME", None))
or bool(os.environ.get("WSL_INTEROP", None))
)

if ON_WSL:
LOG.info("On WSL: Running on WSL detected.")

Check warning on line 69 in src/ansys/mapdl/core/launcher.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/mapdl/core/launcher.py#L69

Added line #L69 was not covered by tests

LOCALHOST = "127.0.0.1"
MAPDL_DEFAULT_PORT = 50052

INTEL_MSG = """Due to incompatibilities between 'DMP', Windows and VPN connections,
INTEL_MSG = """Due to incompatibilities between this MAPDL version, Windows, and VPN connections,
the flat '-mpi INTELMPI' is overwritten by '-mpi msmpi'.

If you still want to use 'INTEL', set:
Expand Down Expand Up @@ -524,7 +531,7 @@
LOG.debug("Checking file error is created")
_check_file_error_created(run_location, timeout)

if os.name == "posix":
if os.name == "posix" and not ON_WSL:
LOG.debug("Checking if gRPC server is alive.")
_check_server_is_alive(stdout_queue, run_location, timeout)

Expand Down Expand Up @@ -1350,15 +1357,38 @@
# Raising error if using non-allowed arguments
if kwargs:
ms_ = ", ".join([f"'{each}'" for each in kwargs.keys()])
raise ValueError(f"The following arguments are not recognaised: {ms_}")
raise ValueError(f"The following arguments are not recognized: {ms_}")

if ip is None:
ip = os.environ.get("PYMAPDL_IP", LOCALHOST)
ip = os.environ.get("PYMAPDL_IP", None)

if not ip and ON_WSL:
ip = _get_windows_host_ip()
if ip:
LOG.debug(

Check warning on line 1368 in src/ansys/mapdl/core/launcher.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/mapdl/core/launcher.py#L1366-L1368

Added lines #L1366 - L1368 were not covered by tests
f"On WSL: Using the following IP address for the Windows OS host: {ip}"
)
else:
LOG.debug(

Check warning on line 1372 in src/ansys/mapdl/core/launcher.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/mapdl/core/launcher.py#L1372

Added line #L1372 was not covered by tests
"PyMAPDL could not find the IP address of the Windows host machine."
)

if not ip:
LOG.debug(
f"No IP address was supplied. Using the default IP address: {LOCALHOST}"
)
ip = LOCALHOST

else:
LOG.debug(
"Because ``PYMAPDL_IP is not None, an attempt is made to connect to a remote session. ('START_INSTANCE' is set to False.`)"
"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
if not ON_WSL:
germa89 marked this conversation as resolved.
Show resolved Hide resolved
start_instance = False

Check warning on line 1388 in src/ansys/mapdl/core/launcher.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/mapdl/core/launcher.py#L1387-L1388

Added lines #L1387 - L1388 were not covered by tests
else:
LOG.debug("On WSL: Allowing 'start_instance' and 'ip' arguments together.")

Check warning on line 1390 in src/ansys/mapdl/core/launcher.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/mapdl/core/launcher.py#L1390

Added line #L1390 was not covered by tests

ip = socket.gethostbyname(ip) # Converting ip or hostname to ip

check_valid_ip(ip) # double check
Expand Down Expand Up @@ -1822,3 +1852,31 @@
)

return version


def _get_windows_host_ip():
output = _run_ip_route()
if output:
return _parse_ip_route(output)

Check warning on line 1860 in src/ansys/mapdl/core/launcher.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/mapdl/core/launcher.py#L1858-L1860

Added lines #L1858 - L1860 were not covered by tests


def _run_ip_route():
from subprocess import run

Check warning on line 1864 in src/ansys/mapdl/core/launcher.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/mapdl/core/launcher.py#L1864

Added line #L1864 was not covered by tests

try:
p = run(["ip", "route"], capture_output=True)
except Exception:
LOG.debug(

Check warning on line 1869 in src/ansys/mapdl/core/launcher.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/mapdl/core/launcher.py#L1866-L1869

Added lines #L1866 - L1869 were not covered by tests
"Detecting the IP address of the host Windows machine requires being able to execute the command 'ip route'."
)
return None

Check warning on line 1872 in src/ansys/mapdl/core/launcher.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/mapdl/core/launcher.py#L1872

Added line #L1872 was not covered by tests

if p and p.stdout and isinstance(p.stdout, bytes):
return p.stdout.decode()

Check warning on line 1875 in src/ansys/mapdl/core/launcher.py

View check run for this annotation

Codecov / codecov/patch

src/ansys/mapdl/core/launcher.py#L1874-L1875

Added lines #L1874 - L1875 were not covered by tests


def _parse_ip_route(output):
match = re.findall(r"(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}).*", output)

if match:
return match[0]
14 changes: 14 additions & 0 deletions tests/test_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
_check_license_argument,
_force_smp_student_version,
_is_ubuntu,
_parse_ip_route,
_validate_MPI,
_verify_version,
find_ansys,
Expand Down Expand Up @@ -410,3 +411,16 @@ def test_launch_mapdl_non_recognaised_arguments():
def test_mapdl_non_recognaised_arguments():
with pytest.raises(ValueError, match="my_fake_argument"):
pymapdl.Mapdl(my_fake_argument="my_fake_value")


def test__parse_ip_route():
output = """default via 172.25.192.1 dev eth0 proto kernel <<<=== this
172.25.192.0/20 dev eth0 proto kernel scope link src 172.25.195.101 <<<=== not this"""

assert "172.25.192.1" == _parse_ip_route(output)

output = """
default via 172.23.112.1 dev eth0 proto kernel
172.23.112.0/20 dev eth0 proto kernel scope link src 172.23.121.145"""

assert "172.23.112.1" == _parse_ip_route(output)
Loading