Skip to content

Commit

Permalink
feat: generic launch_modeler function (#941)
Browse files Browse the repository at this point in the history
  • Loading branch information
RobPasMue authored Jan 22, 2024
1 parent 0026642 commit 26877d3
Show file tree
Hide file tree
Showing 12 changed files with 193 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import pyvista as pv

from ansys.geometry.core import Modeler
from ansys.geometry.core.connection.defaults import GEOMETRY_SERVICE_DOCKER_IMAGE
from ansys.geometry.core.connection.local_instance import LocalDockerInstance
from ansys.geometry.core.connection.docker_instance import LocalDockerInstance
from ansys.geometry.core.math import Point2D
from ansys.geometry.core.misc import UNITS
from ansys.geometry.core.plotting import PlotterHelper
Expand Down Expand Up @@ -61,13 +61,13 @@ for image in available_images:
is_image_available_cont = geom_cont
break

local_instance = LocalDockerInstance(
docker_instance = LocalDockerInstance(
connect_to_existing_service=True,
restart_if_existing_service=True,
image=is_image_available_cont,
)

modeler = Modeler(local_instance=local_instance)
modeler = Modeler(docker_instance=docker_instance)
```

+++
Expand Down
4 changes: 2 additions & 2 deletions doc/source/examples/03_modeling/boolean_operations.mystnb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Perform the required imports.
```{code-cell} ipython3
from typing import List

from ansys.geometry.core import launch_local_modeler
from ansys.geometry.core import launch_docker_modeler
from ansys.geometry.core.designer import Body
from ansys.geometry.core.math import Point2D
from ansys.geometry.core.misc import UNITS
Expand All @@ -37,7 +37,7 @@ modeler, see the "Launch a modeling service" section in the
[PyAnsys Geometry 101: Modeling](../01_getting_started/04_modeling.mystnb) example.

```{code-cell} ipython3
modeler = launch_local_modeler()
modeler = launch_docker_modeler()
```

## Define bodies
Expand Down
2 changes: 1 addition & 1 deletion doc/source/getting_started/docker/common_docker.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ To see the commands for each method, click the following tabs.
modeler = launch_modeler()

The ``launch_modeler()`` method launches the Geometry service under the default
conditions. For more configurability, use the ``launch_local_modeler()`` method.
conditions. For more configurability, use the ``launch_docker_modeler()`` method.

.. tab-item:: Manual launch
:sync: manual
Expand Down
2 changes: 1 addition & 1 deletion src/ansys/geometry/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
import os

from ansys.geometry.core.connection.launcher import (
launch_local_modeler,
launch_docker_modeler,
launch_modeler,
launch_modeler_with_discovery,
launch_modeler_with_discovery_and_pimlight,
Expand Down
12 changes: 6 additions & 6 deletions src/ansys/geometry/core/connection/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,13 @@
DEFAULT_PORT,
GEOMETRY_SERVICE_DOCKER_IMAGE,
)
from ansys.geometry.core.connection.docker_instance import (
GeometryContainers,
LocalDockerInstance,
get_geometry_container_type,
)
from ansys.geometry.core.connection.launcher import (
launch_local_modeler,
launch_docker_modeler,
launch_modeler,
launch_modeler_with_discovery,
launch_modeler_with_discovery_and_pimlight,
Expand All @@ -49,9 +54,4 @@
launch_modeler_with_spaceclaim_and_pimlight,
launch_remote_modeler,
)
from ansys.geometry.core.connection.local_instance import (
GeometryContainers,
LocalDockerInstance,
get_geometry_container_type,
)
from ansys.geometry.core.connection.product_instance import ProductInstance
20 changes: 10 additions & 10 deletions src/ansys/geometry/core/connection/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

from ansys.geometry.core.connection.backend import BackendType
from ansys.geometry.core.connection.defaults import DEFAULT_HOST, DEFAULT_PORT, MAX_MESSAGE_LENGTH
from ansys.geometry.core.connection.local_instance import LocalDockerInstance
from ansys.geometry.core.connection.docker_instance import LocalDockerInstance
from ansys.geometry.core.connection.product_instance import ProductInstance
from ansys.geometry.core.logger import LOG as logger
from ansys.geometry.core.logger import PyGeometryCustomAdapter
Expand Down Expand Up @@ -100,9 +100,9 @@ class GrpcClient:
This instance is deleted when calling the
:func:`GrpcClient.close <ansys.geometry.core.client.GrpcClient.close >`
method.
local_instance : LocalDockerInstance, default: None
Corresponding local instance when the Geometry service is launched using
the ``launch_local_modeler()`` method. This local instance is deleted
docker_instance : LocalDockerInstance, default: None
Corresponding local Docker instance when the Geometry service is launched using
the ``launch_docker_modeler()`` method. This local Docker instance is deleted
when the :func:`GrpcClient.close <ansys.geometry.core.client.GrpcClient.close >`
method is called.
product_instance : ProductInstance, default: None
Expand Down Expand Up @@ -130,7 +130,7 @@ def __init__(
port: Union[str, int] = DEFAULT_PORT,
channel: Optional[grpc.Channel] = None,
remote_instance: Optional["Instance"] = None,
local_instance: Optional[LocalDockerInstance] = None,
docker_instance: Optional[LocalDockerInstance] = None,
product_instance: Optional[ProductInstance] = None,
timeout: Optional[Real] = 120,
logging_level: Optional[int] = logging.INFO,
Expand All @@ -140,7 +140,7 @@ def __init__(
"""Initialize the ``GrpcClient`` object."""
self._closed = False
self._remote_instance = remote_instance
self._local_instance = local_instance
self._docker_instance = docker_instance
self._product_instance = product_instance
if channel:
# Used for PyPIM when directly providing a channel
Expand Down Expand Up @@ -263,14 +263,14 @@ def close(self):
-----
If an instance of the Geometry service was started using
`PyPIM <https://github.com/ansys/pypim>`_, this instance is
deleted. Furthermore, if a local instance
deleted. Furthermore, if a local Docker instance
of the Geometry service was started, it is stopped.
"""
if self._remote_instance:
self._remote_instance.delete() # pragma: no cover
elif self._local_instance:
if not self._local_instance.existed_previously:
self._local_instance.container.stop()
elif self._docker_instance:
if not self._docker_instance.existed_previously:
self._docker_instance.container.stop()
else:
self.log.warning(
"Geometry service was not shut down because it was already running..."
Expand Down
156 changes: 140 additions & 16 deletions src/ansys/geometry/core/connection/launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from ansys.geometry.core.connection.backend import ApiVersions, BackendType
from ansys.geometry.core.connection.client import MAX_MESSAGE_LENGTH
from ansys.geometry.core.connection.defaults import DEFAULT_PIM_CONFIG, DEFAULT_PORT
from ansys.geometry.core.connection.local_instance import (
from ansys.geometry.core.connection.docker_instance import (
_HAS_DOCKER,
GeometryContainers,
LocalDockerInstance,
Expand All @@ -48,15 +48,27 @@
from ansys.geometry.core.modeler import Modeler


def launch_modeler(**kwargs: Optional[Dict]) -> "Modeler":
def launch_modeler(mode: str = None, **kwargs: Optional[Dict]) -> "Modeler":
"""
Start the ``Modeler`` interface for PyAnsys Geometry.
Parameters
----------
mode : str, default: None
Mode in which to launch the ``Modeler`` service. The default is ``None``,
in which case the method tries to determine the mode automatically. The
possible values are:
* ``"pypim"``: Launches the ``Modeler`` service remotely using the PIM API.
* ``"docker"``: Launches the ``Modeler`` service locally using Docker.
* ``"geometry_service"``: Launches the ``Modeler`` service locally using the
Ansys Geometry Service.
* ``"spaceclaim"``: Launches the ``Modeler`` service locally using Ansys SpaceClaim.
* ``"discovery"``: Launches the ``Modeler`` service locally using Ansys Discovery.
**kwargs : dict, default: None
Keyword arguments for the launching methods. For allowable keyword arguments, see the
:func:`launch_remote_modeler` and :func:`launch_local_modeler` methods. Some of these
:func:`launch_remote_modeler` and :func:`launch_docker_modeler` methods. Some of these
keywords might be unused.
Returns
Expand All @@ -71,21 +83,133 @@ def launch_modeler(**kwargs: Optional[Dict]) -> "Modeler":
>>> from ansys.geometry.core import launch_modeler
>>> modeler = launch_modeler()
"""
# A local Docker container of the Geometry service or `PyPIM <https://github.com/ansys/pypim>`_
# is required for this to work. Neither is integrated, but they could possibly be added later.
if mode:
return _launch_with_launchmode(mode, **kwargs)
else:
return _launch_with_automatic_detection(**kwargs)


def _launch_with_launchmode(mode: str, **kwargs: Optional[Dict]) -> "Modeler":
"""
Start the ``Modeler`` interface for PyAnsys Geometry.
Parameters
----------
mode : str
Mode in which to launch the ``Modeler`` service. The possible values are:
* ``"pypim"``: Launches the ``Modeler`` service remotely using the PIM API.
* ``"docker"``: Launches the ``Modeler`` service locally using Docker.
* ``"geometry_service"``: Launches the ``Modeler`` service locally using the
Ansys Geometry Service.
* ``"spaceclaim"``: Launches the ``Modeler`` service locally using Ansys SpaceClaim.
* ``"discovery"``: Launches the ``Modeler`` service locally using Ansys Discovery.
**kwargs : dict, default: None
Keyword arguments for the launching methods. For allowable keyword arguments, see the
:func:`launch_remote_modeler` and :func:`launch_docker_modeler` methods. Some of these
keywords might be unused.
Returns
-------
ansys.geometry.core.modeler.Modeler
Pythonic interface for geometry modeling.
"""
# Ensure that the launch mode is a string
if not isinstance(mode, str):
raise TypeError("The launch mode must be a string.")

# Another alternative is to use this method to run Docker locally.
# Ensure that the launch mode is lowercase
mode = mode.lower()

# Start PyAnsys Geometry with PyPIM if the environment is configured for it
if mode == "pypim":
return launch_remote_modeler(**kwargs)
elif mode == "docker":
return launch_docker_modeler(**kwargs)
elif mode == "geometry_service":
return launch_modeler_with_geometry_service(**kwargs)
elif mode == "spaceclaim":
return launch_modeler_with_spaceclaim(**kwargs)
elif mode == "discovery":
return launch_modeler_with_discovery(**kwargs)
else: # pragma: no cover
raise ValueError(
f"Invalid launch mode '{mode}'. The valid modes are: "
"'pypim', 'docker', 'geometry_service', 'spaceclaim', 'discovery'."
)


def _launch_with_automatic_detection(**kwargs: Optional[Dict]) -> "Modeler":
"""
Start the ``Modeler`` interface for PyAnsys Geometry based on automatic detection.
Parameters
----------
**kwargs : dict, default: None
Keyword arguments for the launching methods. For allowable keyword arguments, see the
:func:`launch_remote_modeler` and :func:`launch_docker_modeler` methods. Some of these
keywords might be unused.
Returns
-------
ansys.geometry.core.modeler.Modeler
Pythonic interface for geometry modeling.
"""
# This method is a wrapper for the other launching methods. It is used to
# determine which method to call based on the environment.
#
# The order of the checks is as follows:
#
# 1. Check if PyPIM is configured and if the environment is configured for it.
# 2. Check if Docker is installed and if the environment is configured for it.
# 3. If you are on a Windows machine:
# - check if the Ansys Geometry service is installed.
# - check if Ansys SpaceClaim is installed.
# - check if Ansys Discovery is installed.

# 1.Start PyAnsys Geometry with PyPIM if the environment is configured for it
# and a directive on how to launch it was not passed.
if _HAS_PIM and pypim.is_configured():
logger.info("Starting Geometry service remotely. The startup configuration is ignored.")
return launch_remote_modeler(**kwargs)

# Otherwise, we are in the "local Docker Container" scenario
if _HAS_DOCKER and LocalDockerInstance.is_docker_installed():
logger.info("Starting Geometry service locally from Docker container.")
return launch_local_modeler(**kwargs)
try:
if _HAS_DOCKER and LocalDockerInstance.is_docker_installed():
logger.info("Starting Geometry service locally from Docker container.")
return launch_docker_modeler(**kwargs)
except Exception:
logger.warning(
"The local Docker container could not be started."
" Trying to start the Geometry service locally."
)

# If we are on a Windows machine, we can try to start the Geometry service locally,
# through various methods: Geometry service, SpaceClaim, Discovery.
if os.name == "nt":
try:
logger.info("Starting Geometry service locally.")
return launch_modeler_with_geometry_service(**kwargs)
except Exception as err:
logger.warning(
"The Geometry service could not be started locally."
" Trying to start Ansys SpaceClaim locally."
)

try:
logger.info("Starting Ansys SpaceClaim with Geometry Service locally.")
return launch_modeler_with_geometry_service(**kwargs)
except Exception as err:
logger.warning(
"Ansys SpaceClaim could not be started locally."
" Trying to start Ansys Discovery locally."
)

try:
logger.info("Starting Ansys Discovery with Geometry Service locally.")
return launch_modeler_with_geometry_service(**kwargs)
except Exception as err:
logger.warning("Ansys SpaceClaim could not be started locally.")

# If we reached this point...
raise NotImplementedError("Geometry service cannot be initialized.")
Expand All @@ -109,7 +233,7 @@ def launch_remote_modeler(version: Optional[str] = None, **kwargs: Optional[Dict
chooses the version.
**kwargs : dict, default: None
Keyword arguments for the launching methods. For allowable keyword arguments, see the
:func:`launch_remote_modeler` and :func:`launch_local_modeler` methods. Some of these
:func:`launch_remote_modeler` and :func:`launch_docker_modeler` methods. Some of these
keywords might be unused.
Returns
Expand All @@ -125,7 +249,7 @@ def launch_remote_modeler(version: Optional[str] = None, **kwargs: Optional[Dict
)


def launch_local_modeler(
def launch_docker_modeler(
port: int = DEFAULT_PORT,
connect_to_existing_service: bool = True,
restart_if_existing_service: bool = False,
Expand Down Expand Up @@ -163,7 +287,7 @@ def launch_local_modeler(
that OS.
**kwargs : dict, default: None
Keyword arguments for the launching methods. For allowable keyword arguments, see the
:func:`launch_remote_modeler` and :func:`launch_local_modeler` methods. Some of these
:func:`launch_remote_modeler` and :func:`launch_docker_modeler` methods. Some of these
keywords might be unused.
Returns
Expand All @@ -177,16 +301,16 @@ def launch_local_modeler(
raise ModuleNotFoundError("The package 'docker' is required to use this function.")

# Call the LocalDockerInstance ctor.
local_instance = LocalDockerInstance(
docker_instance = LocalDockerInstance(
port=port,
connect_to_existing_service=connect_to_existing_service,
restart_if_existing_service=restart_if_existing_service,
name=name,
image=image,
)

# Once the local instance is ready... return the Modeler
return Modeler(host="localhost", port=port, local_instance=local_instance)
# Once the local Docker instance is ready... return the Modeler
return Modeler(host="localhost", port=port, docker_instance=docker_instance)


def launch_modeler_with_discovery_and_pimlight(version: Optional[str] = None) -> "Modeler":
Expand Down
Loading

0 comments on commit 26877d3

Please sign in to comment.