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

Add number of simulations per server #177

Merged
merged 15 commits into from
Nov 29, 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
4 changes: 2 additions & 2 deletions .github/workflows/ci_cd.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: GitHub CI
on:
pull_request:
pull_request_target:
merge_group:
workflow_dispatch:
push:
Expand All @@ -14,7 +15,7 @@ env:
DOCUMENTATION_CNAME: "additive.docs.pyansys.com"
LIBRARY_NAME: "ansys-additive-core"
# NOTE: The server needs to stay in a private registry.
ANSYS_PRODUCT_IMAGE: "ghcr.io/ansys-internal/additive:24.1.0-alpha10"
ANSYS_PRODUCT_IMAGE: "ghcr.io/ansys-internal/additive:24.2.0-alpha1"
ANSYS_PRODUCT_CONTAINER: "ansys-additive-container"

concurrency:
Expand Down Expand Up @@ -89,7 +90,6 @@ jobs:
with:
files: .cov/xml


doc-build:
name: "Building library documentation"
if: github.event_name != 'merge_group'
Expand Down
19 changes: 5 additions & 14 deletions doc/source/contributing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,12 @@ Overall guidance on contributing to a PyAnsys library appears in the
in the *PyAnsys developer's guide*. Ensure that you are thoroughly familiar
with this guide before attempting to contribute to PyAdditive.

The following contribution information is specific to PyAdditive.

Clone the repository
--------------------

To clone and install the latest PyAdditive release in development mode, run
these commands:

.. code::

git clone https://github.com/ansys/pyadditive
cd pyadditive
python -m pip install --upgrade pip
pip install -e .
Configure your development environment
--------------------------------------

For instructions on setting up your development environment, see
:ref:`ref_getting_started`, particularly the :ref:`ref_install_in_developer_mode`
section.

Post issues
-----------
Expand Down
2 changes: 2 additions & 0 deletions doc/source/getting_started/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ Then, run this command to install PyAdditive:

python -m pip install ansys-additive-core

.. _ref_install_in_developer_mode:

Install in developer mode
^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build-backend = "flit_core.buildapi"
[project]
# Check https://flit.readthedocs.io/en/latest/pyproject_toml.html for all available sections
name = "ansys-additive-core"
version = "0.17.dev3"
version = "0.17.dev4"
description = "A Python client for the Ansys Additive service"
readme = "README.rst"
requires-python = ">=3.9,<4"
Expand Down Expand Up @@ -38,7 +38,7 @@ dependencies = [
"panel>=1.2.1",
"platformdirs>=3.8.0",
"plotly>=5.16.1",
"protobuf~=3.20.2",
"protobuf>=3.20.2,<5",
"six>=1.16.0",
"tqdm>=4.45.0",
]
Expand Down Expand Up @@ -119,7 +119,7 @@ show_missing = true

[tool.pytest.ini_options]
minversion = "7.1"
addopts = "-ra --cov=ansys.additive.core --cov-report html:.cov/html --cov-report xml:.cov/xml --cov-report term -vv --cov-fail-under 96"
addopts = "-ra --cov=ansys.additive.core --cov-report html:.cov/html --cov-report xml:.cov/xml --cov-report term -vv --cov-fail-under 95"
testpaths = ["tests"]
filterwarnings = ["ignore:::.*protoc_gen_swagger*"]

Expand Down
72 changes: 63 additions & 9 deletions src/ansys/additive/core/additive.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@
class Additive:
"""Provides the client interface to one or more Additive services.

In a typical cloud environment, a single Additive service with load balancing and
auto-scaling is used. The ``Additive`` client connects to the service via a
single connection. However, for atypical environments or when running on localhost,
the ``Additive`` client can perform crude load balancing by connecting to multiple
servers and distributing simulations across them. You can use the ``server_connections``,
``nservers``, and ``nsims_per_server` parameters to control the
number of servers to connect to and the number of simulations to run on each
server.

Parameters
----------
server_connections: list[str, grpc.Channel], None
Expand All @@ -66,21 +75,49 @@ class Additive:
``server_channels`` or ``channel`` parameters is other than ``None``.
port: int, default: 50052
Port number to use when connecting to the server.
nsims_per_server: int, default: 1
Number of simultaneous simulations to run on each server. Each simulation
requires a license checkout. If a license is not available, the simulation
fails.
nservers: int, default: 1
Number of Additive servers to start and connect to.
``nservers`` instances of Additive server will be started. For this to work,
the Additive portion of the Ansys Structures package must be installed.
This parameter is ignored if the ``server_connections``, ``channel``, or ``host``
parameter is other than ``None``.
Number of Additive servers to start and connect to. This parameter is only
applicable in `PyPIM`_-enabled cloud environments and on localhost. For
this to work on localhost, the Additive portion of the Ansys Structures
package must be installed. This parameter is ignored if the ``server_connections``
parameter or ``host`` parameter is other than ``None``.
product_version: str
Version of the Ansys product installation in the form ``"YYR"``, where ``YY``
is the two-digit year and ``R`` is the release number. For example, the release
2024 R1 would be specified as ``241``. This parameter is only applicable in
PyPIM environments and on localhost.
`PyPIM`_-enabled cloud environments and on localhost. Using an empty string
or ``None`` uses the default product version.
log_level: str, default: "INFO"
Minimum severity level of messages to log.
log_file: str, default: ""
File name to write log messages to.

Examples
--------
Connect to a list of servers. Multiple connections to the same host are permitted.

>>> additive = Additive(server_connections=["localhost:50052", "localhost:50052", "myserver:50052"])

Connect to a single server using the host name and port number.

>>> additive = Additive(host="additive.ansys.com", port=12345)

Start and connect to two servers on localhost or in a
`PyPIM`_-enabled cloud environment. Allow each server to run two
simultaneous simulations.

>>> additive = Additive(nsims_per_server=2, nservers=2)

Start a single server on localhost or in a `PyPIM`_-enabled cloud environment.
Use version 2024 R1 of the Ansys product installation.

>>> additive = Additive(product_version="241")

.. _PyPIM: https://pypim.docs.pyansys.com/version/stable/index.html
"""

DEFAULT_ADDITIVE_SERVICE_PORT = 50052
Expand All @@ -90,18 +127,23 @@ def __init__(
server_connections: list[str | grpc.Channel] = None,
host: str | None = None,
port: int = DEFAULT_ADDITIVE_SERVICE_PORT,
nsims_per_server: int = 1,
nservers: int = 1,
product_version: str = DEFAULT_PRODUCT_VERSION,
log_level: str = "INFO",
log_file: str = "",
) -> None:
"""Initialize server connections."""
if product_version is None or product_version == "":
product_version = DEFAULT_PRODUCT_VERSION

self._log = Additive._create_logger(log_file, log_level)
self._log.debug("Logging set to %s", log_level)

self._servers = Additive._connect_to_servers(
server_connections, host, port, nservers, product_version, self._log
)
self._nsims_per_server = nsims_per_server

# Setup data directory
self._user_data_path = USER_DATA_PATH
Expand Down Expand Up @@ -158,14 +200,25 @@ def _connect_to_servers(

return connections

@property
def nsims_per_server(self) -> int:
"""Number of simultaneous simulations to run on each server."""
return self._nsims_per_server

@nsims_per_server.setter
def nsims_per_server(self, value: int) -> None:
"""Set the number of simultaneous simulations to run on each server."""
if value < 1:
raise ValueError("Number of simulations per server must be greater than zero.")
self._nsims_per_server = value

def about(self) -> None:
"""Print information about the client and server."""
print(f"Client {__version__}, API version: {api_version}")
if self._servers is None:
print("Not connected to server")
print("Client is not connected to a server.")
return
else:
print("_servers not None")
for server in self._servers:
print(server.status())

Expand Down Expand Up @@ -206,7 +259,8 @@ def simulate(
f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} Completed 0 of {len(inputs)} simulations",
end="",
)
with concurrent.futures.ThreadPoolExecutor(len(self._servers)) as executor:
threads = min(len(inputs), len(self._servers) * self._nsims_per_server)
with concurrent.futures.ThreadPoolExecutor(threads) as executor:
futures = []
for i, input in enumerate(inputs):
server_id = i % len(self._servers)
Expand Down
12 changes: 7 additions & 5 deletions src/ansys/additive/core/server_connection/server_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,11 @@ class ServerConnectionStatus:
class ServerConnection:
"""Provides connection to Additive server.

If neither ``channel`` nor ``addr`` are provided, an attempt will be
made to start an Additive server and connect to it. If running in a
cloud environment, :class:`PyPIM <ansys.platform.instancemanagement.pypim>`
must be supported. If running on localhost, the Additive option of the
If neither ``channel`` nor ``addr`` are provided, an attempt is
made to start an Additive server and connect to it. Starting a server
in a cloud environment requires
`PyPIM <https://pypim.docs.pyansys.com/version/stable/index.html>`_ to be available.
To start a server when running on localhost, the Additive option of the
Structures package of the Ansys unified installation must be installed.

Parameters
Expand All @@ -80,7 +81,8 @@ class ServerConnection:
Version of the Ansys product installation in the form ``"YYR"``, where ``YY``
is the two-digit year and ``R`` is the release number. For example, the release
2024 R1 would be specified as ``241``. This parameter is only applicable in
PyPIM environments and on localhost.
`PyPIM <https://pypim.docs.pyansys.com/version/stable/index.html>`_-enabled
cloud environments and on localhost.
log: logging.Logger, None
Log to write connection messages to.
"""
Expand Down
4 changes: 2 additions & 2 deletions tests/server_connection/test_local_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,11 @@ def test_launch_with_linux_installation_but_invalid_ansys_version_raises_excepti
@pytest.mark.skipif(os.name != "nt", reason="Test only valid on Windows")
def test_launch_when_exe_not_found_raises_exception_win():
# arrange
os.environ["AWP_ROOT241"] = "Bogus"
os.environ["AWP_ROOT123"] = "Bogus"

# act, assert
with pytest.raises(FileNotFoundError) as excinfo:
LocalServer.launch(TEST_VALID_PORT)
LocalServer.launch(TEST_VALID_PORT, product_version="123")
assert "Cannot find " in str(excinfo.value)


Expand Down
Loading